zammy 1.2.0 → 1.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/README.md +328 -214
- package/SECURITY.md +57 -0
- package/assets/zammy.gif +0 -0
- package/dist/index.js +3257 -450
- package/package.json +10 -3
- package/packages/plugins/docker/README.md +141 -0
- package/packages/plugins/docker/dist/index.d.ts +46 -0
- package/packages/plugins/docker/dist/index.d.ts.map +1 -0
- package/packages/plugins/docker/dist/index.js +402 -0
- package/packages/plugins/docker/dist/index.js.map +1 -0
- package/packages/plugins/docker/package.json +28 -0
- package/packages/plugins/docker/zammy-plugin.json +16 -0
- package/packages/plugins/faker/README.md +65 -0
- package/packages/plugins/faker/dist/index.d.ts +43 -0
- package/packages/plugins/faker/dist/index.d.ts.map +1 -0
- package/packages/plugins/faker/dist/index.js +349 -0
- package/packages/plugins/faker/dist/index.js.map +1 -0
- package/packages/plugins/faker/package.json +28 -0
- package/packages/plugins/faker/zammy-plugin.json +14 -0
- package/packages/plugins/network/README.md +126 -0
- package/packages/plugins/network/dist/index.d.ts +45 -0
- package/packages/plugins/network/dist/index.d.ts.map +1 -0
- package/packages/plugins/network/dist/index.js +406 -0
- package/packages/plugins/network/dist/index.js.map +1 -0
- package/packages/plugins/network/package.json +28 -0
- package/packages/plugins/network/zammy-plugin.json +17 -0
- package/packages/plugins/port/README.md +74 -0
- package/packages/plugins/port/dist/index.d.ts +47 -0
- package/packages/plugins/port/dist/index.d.ts.map +1 -0
- package/packages/plugins/port/dist/index.js +331 -0
- package/packages/plugins/port/dist/index.js.map +1 -0
- package/packages/plugins/port/package.json +28 -0
- package/packages/plugins/port/zammy-plugin.json +16 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import * as
|
|
4
|
+
import * as readline4 from "readline";
|
|
5
5
|
|
|
6
6
|
// src/ui/banner.ts
|
|
7
7
|
import figlet from "figlet";
|
|
8
|
+
import chalk3 from "chalk";
|
|
8
9
|
|
|
9
10
|
// src/ui/colors.ts
|
|
10
11
|
import chalk from "chalk";
|
|
@@ -180,6 +181,15 @@ var box = {
|
|
|
180
181
|
return theme.dim(`${chars.tl}${chars.h.repeat(leftPad)} `) + title + theme.dim(` ${chars.h.repeat(rightPad)}${chars.tr}`);
|
|
181
182
|
}
|
|
182
183
|
};
|
|
184
|
+
var progressBar = (percent, width = 30, showPercent = true) => {
|
|
185
|
+
const filled = Math.round(percent / 100 * width);
|
|
186
|
+
const empty = width - filled;
|
|
187
|
+
let color = theme.success;
|
|
188
|
+
if (percent > 70) color = theme.warning;
|
|
189
|
+
if (percent > 90) color = theme.error;
|
|
190
|
+
const bar = color("\u2588".repeat(filled)) + theme.dim("\u2591".repeat(empty));
|
|
191
|
+
return showPercent ? `${bar} ${percent.toFixed(0)}%` : bar;
|
|
192
|
+
};
|
|
183
193
|
var bubble = {
|
|
184
194
|
say: (text, width = 50) => {
|
|
185
195
|
const lines = [];
|
|
@@ -227,34 +237,390 @@ var categoryIcons = {
|
|
|
227
237
|
};
|
|
228
238
|
var spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
229
239
|
|
|
240
|
+
// src/ui/slime-animated.ts
|
|
241
|
+
import chalk2 from "chalk";
|
|
242
|
+
var C = {
|
|
243
|
+
P1: "#4A235A",
|
|
244
|
+
// Darkest edge
|
|
245
|
+
P2: "#7D3C98",
|
|
246
|
+
// Dark purple
|
|
247
|
+
P3: "#9B59B6",
|
|
248
|
+
// Main purple
|
|
249
|
+
P4: "#BB8FCE",
|
|
250
|
+
// Light purple
|
|
251
|
+
P5: "#D7BDE2",
|
|
252
|
+
// Lightest highlight
|
|
253
|
+
WH: "#FFFFFF",
|
|
254
|
+
// White (sparkles, eye shine)
|
|
255
|
+
BK: "#1A1A2E",
|
|
256
|
+
// Black (eyes, mouth)
|
|
257
|
+
PK: "#E91E63",
|
|
258
|
+
// Pink (tongue)
|
|
259
|
+
YL: "#F1C40F",
|
|
260
|
+
// Yellow (stars)
|
|
261
|
+
BL: "#3498DB",
|
|
262
|
+
// Blue (tears)
|
|
263
|
+
CY: "#00CED1",
|
|
264
|
+
// Cyan (zzz)
|
|
265
|
+
RD: "#E74C3C"
|
|
266
|
+
// Red (angry)
|
|
267
|
+
};
|
|
268
|
+
var b = (hex) => chalk2.bgHex(hex)(" ");
|
|
269
|
+
var dot = chalk2.hex(C.WH)("\xB7");
|
|
270
|
+
var star = chalk2.hex(C.WH)("\u2726");
|
|
271
|
+
var heart = chalk2.hex(C.PK)("\u2665");
|
|
272
|
+
var z = chalk2.hex(C.CY)("z");
|
|
273
|
+
var Z = chalk2.hex(C.CY)("Z");
|
|
274
|
+
var eye = chalk2.bgHex(C.BK).hex(C.WH)(" \xB7");
|
|
275
|
+
var shine = chalk2.bgHex(C.P3).hex(C.WH)(" \xB7");
|
|
276
|
+
var shine2 = chalk2.bgHex(C.P4).hex(C.WH)("\xB7 ");
|
|
277
|
+
var slimeFrames = {
|
|
278
|
+
happy: () => {
|
|
279
|
+
const { P1, P2, P3, P4, P5, BK, PK } = C;
|
|
280
|
+
return [
|
|
281
|
+
` ${dot} ${dot}`,
|
|
282
|
+
` ${b(P5)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P5)}`,
|
|
283
|
+
` ${b(P5)}${b(P4)}${b(P3)}${shine2}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}${b(P5)}`,
|
|
284
|
+
` ${b(P4)}${b(P3)}${eye}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${eye}${b(P3)}${b(P4)}`,
|
|
285
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
286
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(PK)}${b(BK)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
287
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P2)}`,
|
|
288
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
289
|
+
];
|
|
290
|
+
},
|
|
291
|
+
excited: () => {
|
|
292
|
+
const { P1, P2, P3, P4, P5, BK, PK, YL } = C;
|
|
293
|
+
const starEye = chalk2.bgHex(YL).hex(C.WH)("\u2605 ");
|
|
294
|
+
return [
|
|
295
|
+
` ${star} ${dot} ${star} ${dot} ${star}`,
|
|
296
|
+
` ${b(P5)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P5)}`,
|
|
297
|
+
` ${b(P5)}${b(P4)}${b(P3)}${shine2}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}${b(P5)}`,
|
|
298
|
+
` ${b(P4)}${b(P3)}${starEye}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${starEye}${b(P3)}${b(P4)}`,
|
|
299
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
300
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(PK)}${b(BK)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
301
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P2)}`,
|
|
302
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
303
|
+
];
|
|
304
|
+
},
|
|
305
|
+
love: () => {
|
|
306
|
+
const { P1, P2, P3, P4, P5, BK, PK } = C;
|
|
307
|
+
const heartEye = chalk2.bgHex(PK).hex(C.WH)("\u2665 ");
|
|
308
|
+
return [
|
|
309
|
+
` ${heart} ${heart}`,
|
|
310
|
+
` ${b(P5)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P5)}`,
|
|
311
|
+
` ${b(P5)}${b(P4)}${b(P3)}${shine2}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}${b(P5)}`,
|
|
312
|
+
` ${b(P4)}${b(P3)}${heartEye}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${heartEye}${b(P3)}${b(P4)}`,
|
|
313
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
314
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(PK)}${b(BK)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
315
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P2)}`,
|
|
316
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
317
|
+
];
|
|
318
|
+
},
|
|
319
|
+
sleepy: () => {
|
|
320
|
+
const { P1, P2, P3, P4, P5, BK } = C;
|
|
321
|
+
const closedEye = chalk2.bgHex(P3).hex(BK)("\u2500\u2500");
|
|
322
|
+
return [
|
|
323
|
+
` ${z} ${z} ${Z}`,
|
|
324
|
+
` ${b(P5)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P5)}`,
|
|
325
|
+
` ${b(P5)}${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}${b(P5)}`,
|
|
326
|
+
` ${b(P4)}${b(P3)}${closedEye}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${closedEye}${b(P3)}${b(P4)}`,
|
|
327
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
328
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
329
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P2)}`,
|
|
330
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
331
|
+
];
|
|
332
|
+
},
|
|
333
|
+
thinking: () => {
|
|
334
|
+
const { P1, P2, P3, P4, P5, BK, YL } = C;
|
|
335
|
+
const thinkEye = chalk2.bgHex(C.BK).hex(C.WH)(" \xB7");
|
|
336
|
+
const lookUpEye = chalk2.bgHex(C.BK).hex(C.WH)("\xB7 ");
|
|
337
|
+
return [
|
|
338
|
+
` ${chalk2.hex(YL)("?")}`,
|
|
339
|
+
` ${b(P5)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P5)}`,
|
|
340
|
+
` ${b(P5)}${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}${b(P5)}`,
|
|
341
|
+
` ${b(P4)}${b(P3)}${thinkEye}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${lookUpEye}${b(P3)}${b(P4)}`,
|
|
342
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
343
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
344
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P2)}`,
|
|
345
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
346
|
+
];
|
|
347
|
+
},
|
|
348
|
+
surprised: () => {
|
|
349
|
+
const { P1, P2, P3, P4, P5, BK, YL } = C;
|
|
350
|
+
const bigEye = chalk2.bgHex(BK).hex(C.WH)("\u25C9 ");
|
|
351
|
+
return [
|
|
352
|
+
` ${chalk2.hex(YL)("!!")}`,
|
|
353
|
+
` ${b(P5)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P5)}`,
|
|
354
|
+
` ${b(P5)}${b(P4)}${b(P3)}${shine2}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}${b(P5)}`,
|
|
355
|
+
` ${b(P4)}${b(P3)}${bigEye}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${bigEye}${b(P3)}${b(P4)}`,
|
|
356
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
357
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(BK)}${b(BK)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
358
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P2)}`,
|
|
359
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
360
|
+
];
|
|
361
|
+
},
|
|
362
|
+
sad: () => {
|
|
363
|
+
const { P1, P2, P3, P4, P5, BK, BL } = C;
|
|
364
|
+
const sadEye = chalk2.bgHex(BK).hex(C.WH)(" \xB7");
|
|
365
|
+
const tear = chalk2.bgHex(BL)(" ");
|
|
366
|
+
return [
|
|
367
|
+
` ${dot} ${dot} ${dot}`,
|
|
368
|
+
` ${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}`,
|
|
369
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
370
|
+
` ${b(P4)}${b(P3)}${sadEye}${tear}${b(P3)}${b(P3)}${tear}${sadEye}${b(P3)}${b(P4)}`,
|
|
371
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
372
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(BK)}${b(BK)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
373
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P2)}`,
|
|
374
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
375
|
+
];
|
|
376
|
+
},
|
|
377
|
+
wink: () => {
|
|
378
|
+
const { P1, P2, P3, P4, P5, BK, PK } = C;
|
|
379
|
+
const winkEye = chalk2.bgHex(P3).hex(BK)("> ");
|
|
380
|
+
return [
|
|
381
|
+
` ${star} ${dot}`,
|
|
382
|
+
` ${b(P5)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P5)}`,
|
|
383
|
+
` ${b(P5)}${b(P4)}${b(P3)}${shine2}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}${b(P5)}`,
|
|
384
|
+
` ${b(P4)}${b(P3)}${eye}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${winkEye}${b(P3)}${b(P4)}`,
|
|
385
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
386
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(PK)}${b(BK)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
387
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${shine}${b(P3)}${b(P3)}${b(P2)}`,
|
|
388
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
389
|
+
];
|
|
390
|
+
},
|
|
391
|
+
angry: () => {
|
|
392
|
+
const { P1, P2, P3, P4, BK, RD } = C;
|
|
393
|
+
const angryEye = chalk2.bgHex(BK).hex(RD)("> ");
|
|
394
|
+
const angryEye2 = chalk2.bgHex(BK).hex(RD)(" <");
|
|
395
|
+
return [
|
|
396
|
+
` ${chalk2.hex(RD)("# @!")}`,
|
|
397
|
+
` ${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}${b(P4)}`,
|
|
398
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
399
|
+
` ${b(P4)}${b(P3)}${angryEye}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${angryEye2}${b(P3)}${b(P4)}`,
|
|
400
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
401
|
+
` ${b(P4)}${b(P3)}${b(P3)}${b(P3)}${b(BK)}${b(BK)}${b(BK)}${b(P3)}${b(P3)}${b(P4)}`,
|
|
402
|
+
` ${b(P2)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P3)}${b(P2)}`,
|
|
403
|
+
` ${b(P1)}${b(P2)}${b(P2)}${b(P2)}${b(P2)}${b(P1)}`
|
|
404
|
+
];
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
var greetings = [
|
|
408
|
+
"Hiii~! I'm Zammy!",
|
|
409
|
+
"Ready to get stuff done?",
|
|
410
|
+
"*bounces* What's the plan?",
|
|
411
|
+
"Type / to see commands!",
|
|
412
|
+
"*jiggles excitedly* Let's go!",
|
|
413
|
+
"*sparkles* Hi friend!"
|
|
414
|
+
];
|
|
415
|
+
var lateNightGreetings = [
|
|
416
|
+
"*yawns* Burning the midnight oil?",
|
|
417
|
+
"zzZ... oh! You're still up?",
|
|
418
|
+
"*sleepy bounce* Late night coding?",
|
|
419
|
+
"The best code happens at 3am~"
|
|
420
|
+
];
|
|
421
|
+
var morningGreetings = [
|
|
422
|
+
"*stretches* Good morning~!",
|
|
423
|
+
"Rise and shine!",
|
|
424
|
+
"*bounces awake* New day!",
|
|
425
|
+
"Morning! Coffee first?"
|
|
426
|
+
];
|
|
427
|
+
function getGreeting() {
|
|
428
|
+
const hour = (/* @__PURE__ */ new Date()).getHours();
|
|
429
|
+
if (hour >= 0 && hour < 5) {
|
|
430
|
+
return lateNightGreetings[Math.floor(Math.random() * lateNightGreetings.length)];
|
|
431
|
+
} else if (hour >= 5 && hour < 12) {
|
|
432
|
+
return morningGreetings[Math.floor(Math.random() * morningGreetings.length)];
|
|
433
|
+
}
|
|
434
|
+
return greetings[Math.floor(Math.random() * greetings.length)];
|
|
435
|
+
}
|
|
436
|
+
function getStartupMood() {
|
|
437
|
+
const hour = (/* @__PURE__ */ new Date()).getHours();
|
|
438
|
+
if (hour >= 0 && hour < 5) return "sleepy";
|
|
439
|
+
return "happy";
|
|
440
|
+
}
|
|
441
|
+
var miniSlime = {
|
|
442
|
+
happy: chalk2.hex(C.P3)("(") + chalk2.hex(C.BK)("\u25D5") + chalk2.hex(C.P3)("\u1D17") + chalk2.hex(C.BK)("\u25D5") + chalk2.hex(C.P3)(")"),
|
|
443
|
+
excited: chalk2.hex(C.P3)("(") + chalk2.hex(C.YL)("\u2605") + chalk2.hex(C.P3)("\u1D17") + chalk2.hex(C.YL)("\u2605") + chalk2.hex(C.P3)(")"),
|
|
444
|
+
love: chalk2.hex(C.P3)("(") + chalk2.hex(C.PK)("\u2665") + chalk2.hex(C.P3)("\u1D17") + chalk2.hex(C.PK)("\u2665") + chalk2.hex(C.P3)(")"),
|
|
445
|
+
sleepy: chalk2.hex(C.P3)("(") + chalk2.hex(C.BK)("\u2013") + chalk2.hex(C.P3)("\u03C9") + chalk2.hex(C.BK)("\u2013") + chalk2.hex(C.P3)(") zZ"),
|
|
446
|
+
sad: chalk2.hex(C.P3)("(") + chalk2.hex(C.BK)("\u25D5") + chalk2.hex(C.P3)("\uFE35") + chalk2.hex(C.BK)("\u25D5") + chalk2.hex(C.P3)(")"),
|
|
447
|
+
surprised: chalk2.hex(C.P3)("(") + chalk2.hex(C.BK)("\u25CE") + chalk2.hex(C.P3)("\u25CB") + chalk2.hex(C.BK)("\u25CE") + chalk2.hex(C.P3)(")!"),
|
|
448
|
+
wink: chalk2.hex(C.P3)("(") + chalk2.hex(C.BK)("\u25D5") + chalk2.hex(C.P3)("\u1D17") + chalk2.hex(C.BK)(">") + chalk2.hex(C.P3)(")"),
|
|
449
|
+
thinking: chalk2.hex(C.P3)("(") + chalk2.hex(C.BK)("\u25D5") + chalk2.hex(C.P3)("\uFF5E") + chalk2.hex(C.BK)("\u25D4") + chalk2.hex(C.P3)(")"),
|
|
450
|
+
angry: chalk2.hex(C.P3)("(") + chalk2.hex(C.RD)(">") + chalk2.hex(C.P3)("_") + chalk2.hex(C.RD)("<") + chalk2.hex(C.P3)(")")
|
|
451
|
+
};
|
|
452
|
+
var validMoods = Object.keys(slimeFrames);
|
|
453
|
+
function react(mood, message) {
|
|
454
|
+
const slime = miniSlime[mood] || miniSlime.happy;
|
|
455
|
+
if (message) {
|
|
456
|
+
console.log(` ${slime} ${message}`);
|
|
457
|
+
} else {
|
|
458
|
+
console.log(` ${slime}`);
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
var MIN_WIDTH_FOR_MASCOT = 50;
|
|
462
|
+
function showMascot(mood) {
|
|
463
|
+
const termWidth = process.stdout.columns || 80;
|
|
464
|
+
if (termWidth < MIN_WIDTH_FOR_MASCOT) {
|
|
465
|
+
react(mood);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
const frameFunc = slimeFrames[mood];
|
|
469
|
+
if (frameFunc) {
|
|
470
|
+
const art = frameFunc();
|
|
471
|
+
console.log("");
|
|
472
|
+
art.forEach((line) => console.log(" " + line));
|
|
473
|
+
console.log("");
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function getMoodFromText(text) {
|
|
477
|
+
const lower = text.toLowerCase();
|
|
478
|
+
if (/\b(thanks|thank you|love|love you|thx|ty|<3|heart)\b/.test(lower)) {
|
|
479
|
+
return "love";
|
|
480
|
+
}
|
|
481
|
+
if (/\b(awesome|amazing|wow|cool|nice|great|perfect|yay|woohoo|!\s*$)\b/.test(lower)) {
|
|
482
|
+
return "excited";
|
|
483
|
+
}
|
|
484
|
+
if (/\b(tired|sleepy|zzz|night|late|exhausted)\b/.test(lower)) {
|
|
485
|
+
return "sleepy";
|
|
486
|
+
}
|
|
487
|
+
if (/\b(angry|mad|hate|stupid|wtf|damn|crap|sucks|ugh|argh|grr)\b|:\(/.test(lower)) {
|
|
488
|
+
return "angry";
|
|
489
|
+
}
|
|
490
|
+
if (/\b(sad|sorry|fail|broke|wrong)\b/.test(lower)) {
|
|
491
|
+
return "sad";
|
|
492
|
+
}
|
|
493
|
+
if (/\b(hmm|think|wonder|maybe|idk|not sure|confused|\?$)\b/.test(lower)) {
|
|
494
|
+
return "thinking";
|
|
495
|
+
}
|
|
496
|
+
return null;
|
|
497
|
+
}
|
|
498
|
+
|
|
230
499
|
// src/ui/banner.ts
|
|
231
|
-
|
|
232
|
-
|
|
500
|
+
var C2 = {
|
|
501
|
+
// Purple gradient (light to dark)
|
|
502
|
+
P1: "#4A235A",
|
|
503
|
+
// Darkest edge
|
|
504
|
+
P2: "#7D3C98",
|
|
505
|
+
// Dark purple
|
|
506
|
+
P3: "#9B59B6",
|
|
507
|
+
// Main purple
|
|
508
|
+
P4: "#BB8FCE",
|
|
509
|
+
// Light purple
|
|
510
|
+
P5: "#D7BDE2",
|
|
511
|
+
// Lightest
|
|
512
|
+
// Accents
|
|
513
|
+
WH: "#FFFFFF",
|
|
514
|
+
// White (sparkles, eye highlight)
|
|
515
|
+
BK: "#1A1A2E",
|
|
516
|
+
// Black (eyes, mouth)
|
|
517
|
+
PK: "#E91E63"
|
|
518
|
+
// Pink (tongue)
|
|
519
|
+
};
|
|
520
|
+
var b2 = (hex) => chalk3.bgHex(hex)(" ");
|
|
521
|
+
var dot2 = chalk3.hex(C2.WH)("\xB7");
|
|
522
|
+
function getSlimeLines(isBlinking, _mood) {
|
|
523
|
+
const { P1, P2, P3, P4, P5, WH, BK, PK } = C2;
|
|
524
|
+
const openEye = chalk3.bgHex(BK).hex(WH)(" \xB7");
|
|
525
|
+
const closedEye = chalk3.bgHex(P3).hex(BK)("\u2500\u2500");
|
|
526
|
+
const eyeL = isBlinking ? closedEye : openEye;
|
|
527
|
+
const eyeR = isBlinking ? closedEye : openEye;
|
|
528
|
+
const shine3 = chalk3.bgHex(P3).hex(WH)(" \xB7");
|
|
529
|
+
const shine22 = chalk3.bgHex(P4).hex(WH)("\xB7 ");
|
|
530
|
+
const miniBlob = `${b2(P3)}`;
|
|
531
|
+
return [
|
|
532
|
+
` ${dot2} ${dot2}`,
|
|
533
|
+
` ${b2(P5)}${b2(P4)}${b2(P4)}${b2(P4)}${b2(P4)}${b2(P4)}${b2(P4)}${b2(P5)}`,
|
|
534
|
+
` ${b2(P5)}${b2(P4)}${b2(P3)}${shine22}${b2(P3)}${b2(P3)}${b2(P3)}${b2(P3)}${b2(P4)}${b2(P5)}`,
|
|
535
|
+
` ${b2(P4)}${b2(P3)}${eyeL}${b2(P3)}${b2(P3)}${b2(P3)}${b2(P3)}${eyeR}${b2(P3)}${b2(P4)}`,
|
|
536
|
+
` ${b2(P4)}${b2(P3)}${b2(P3)}${b2(P3)}${shine3}${b2(P3)}${b2(P3)}${b2(P3)}${b2(P3)}${b2(P4)}`,
|
|
537
|
+
` ${b2(P4)}${b2(P3)}${b2(P3)}${b2(P3)}${b2(BK)}${b2(PK)}${b2(BK)}${b2(P3)}${b2(P3)}${b2(P4)}`,
|
|
538
|
+
` ${b2(P2)}${b2(P3)}${b2(P3)}${b2(P3)}${shine3}${b2(P3)}${b2(P3)}${b2(P2)}`,
|
|
539
|
+
` ${b2(P1)}${b2(P2)}${b2(P2)}${b2(P2)}${b2(P2)}${b2(P1)} ${miniBlob}`
|
|
540
|
+
];
|
|
541
|
+
}
|
|
542
|
+
var MIN_WIDTH_FOR_MASCOT2 = 90;
|
|
543
|
+
var MIN_WIDTH_FOR_FULL_LOGO = 55;
|
|
544
|
+
var MIN_WIDTH_FOR_COMPACT = 30;
|
|
545
|
+
async function displayBanner(simple = false) {
|
|
546
|
+
return new Promise((resolve6) => {
|
|
547
|
+
const termWidth = process.stdout.columns || 80;
|
|
233
548
|
figlet("ZAMMY", {
|
|
234
549
|
font: "ANSI Shadow",
|
|
235
550
|
horizontalLayout: "default"
|
|
236
|
-
}, (err, data) => {
|
|
551
|
+
}, async (err, data) => {
|
|
237
552
|
console.log("");
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
console.log(theme.gradient("
|
|
242
|
-
console.log(theme.
|
|
243
|
-
console.log(theme.
|
|
244
|
-
console.log(
|
|
553
|
+
const greeting = getGreeting();
|
|
554
|
+
const mood = getStartupMood();
|
|
555
|
+
if (termWidth < MIN_WIDTH_FOR_COMPACT) {
|
|
556
|
+
console.log(theme.gradient(" ZAMMY"));
|
|
557
|
+
console.log(theme.secondary(` "${greeting}"`));
|
|
558
|
+
console.log(theme.dim(" Type /help for commands"));
|
|
559
|
+
console.log("");
|
|
560
|
+
resolve6();
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
let figletLines = [];
|
|
564
|
+
if (err || !data || termWidth < MIN_WIDTH_FOR_FULL_LOGO) {
|
|
565
|
+
if (termWidth < MIN_WIDTH_FOR_FULL_LOGO) {
|
|
566
|
+
figletLines = [
|
|
567
|
+
"\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557",
|
|
568
|
+
"\u2551 Z A M M Y \u2551",
|
|
569
|
+
"\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D"
|
|
570
|
+
];
|
|
571
|
+
} else {
|
|
572
|
+
figletLines = [
|
|
573
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557",
|
|
574
|
+
"\u255A\u2550\u2550\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2551\u255A\u2588\u2588\u2557 \u2588\u2588\u2554\u255D",
|
|
575
|
+
" \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551\u2588\u2588\u2554\u2588\u2588\u2588\u2588\u2554\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2554\u255D ",
|
|
576
|
+
" \u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551\u2588\u2588\u2551\u255A\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u255A\u2588\u2588\u2554\u255D ",
|
|
577
|
+
"\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551\u2588\u2588\u2551 \u255A\u2550\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 ",
|
|
578
|
+
"\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D "
|
|
579
|
+
];
|
|
580
|
+
}
|
|
245
581
|
} else {
|
|
246
|
-
data.split("\n").
|
|
247
|
-
console.log(theme.gradient(line));
|
|
248
|
-
});
|
|
582
|
+
figletLines = data.split("\n").filter((line) => line.trim());
|
|
249
583
|
}
|
|
584
|
+
const showMascot2 = !simple && termWidth >= MIN_WIDTH_FOR_MASCOT2;
|
|
585
|
+
if (!showMascot2) {
|
|
586
|
+
figletLines.forEach((line) => console.log(" " + theme.gradient(line)));
|
|
587
|
+
console.log("");
|
|
588
|
+
console.log(theme.secondary(` "${greeting}"`));
|
|
589
|
+
console.log("");
|
|
590
|
+
console.log(theme.dim(` ${symbols.arrow} Type ${theme.primary("/")} to browse commands or ${theme.primary("/help")} for full list`));
|
|
591
|
+
console.log(theme.dim(` ${symbols.arrow} Shell commands start with ${theme.primary("!")} (e.g., ${theme.primary("!ls")}, ${theme.primary("!git")})`));
|
|
592
|
+
console.log("");
|
|
593
|
+
resolve6();
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
const figletWidth = 50;
|
|
597
|
+
const paddedFiglet = figletLines.map((line) => line.padEnd(figletWidth));
|
|
598
|
+
const blinkSequence = [false, false, false, true, false, false];
|
|
599
|
+
const frameTime = 180;
|
|
600
|
+
process.stdout.write("\x1B[?25l");
|
|
601
|
+
for (let frame = 0; frame < blinkSequence.length; frame++) {
|
|
602
|
+
const isBlinking = blinkSequence[frame];
|
|
603
|
+
const slimeLines = getSlimeLines(isBlinking, mood);
|
|
604
|
+
if (frame > 0) {
|
|
605
|
+
const totalLines = Math.max(paddedFiglet.length, slimeLines.length) + 1;
|
|
606
|
+
process.stdout.write(`\x1B[${totalLines}A`);
|
|
607
|
+
}
|
|
608
|
+
const maxLines = Math.max(paddedFiglet.length, slimeLines.length);
|
|
609
|
+
for (let i = 0; i < maxLines; i++) {
|
|
610
|
+
const figLine = paddedFiglet[i] || "".padEnd(figletWidth);
|
|
611
|
+
const slimeLine = slimeLines[i] || "";
|
|
612
|
+
console.log(" " + theme.gradient(figLine) + " " + slimeLine);
|
|
613
|
+
}
|
|
614
|
+
console.log("");
|
|
615
|
+
await new Promise((r) => setTimeout(r, frameTime));
|
|
616
|
+
}
|
|
617
|
+
process.stdout.write("\x1B[?25h");
|
|
618
|
+
console.log(theme.secondary(` "${greeting}"`));
|
|
250
619
|
console.log("");
|
|
251
|
-
console.log(` ${symbols.
|
|
252
|
-
console.log("");
|
|
253
|
-
console.log(theme.dim(` ${symbols.arrow} Type ${theme.primary("/")} to browse commands (use ${theme.primary("\u2191\u2193")} to navigate, ${theme.primary("Tab")} to select)`));
|
|
254
|
-
console.log(theme.dim(` ${symbols.arrow} Type ${theme.primary("/help")} for full command list`));
|
|
255
|
-
console.log(theme.dim(` ${symbols.arrow} Shell commands start with ${theme.primary("!")} (e.g., ${theme.primary("!ls")}, ${theme.primary("!cd")})`));
|
|
620
|
+
console.log(theme.dim(` ${symbols.arrow} Type ${theme.primary("/")} to browse commands or ${theme.primary("/help")} for full list`));
|
|
621
|
+
console.log(theme.dim(` ${symbols.arrow} Shell commands start with ${theme.primary("!")} (e.g., ${theme.primary("!ls")}, ${theme.primary("!git")})`));
|
|
256
622
|
console.log("");
|
|
257
|
-
|
|
623
|
+
resolve6();
|
|
258
624
|
});
|
|
259
625
|
});
|
|
260
626
|
}
|
|
@@ -267,7 +633,17 @@ function getPrompt() {
|
|
|
267
633
|
// src/commands/registry.ts
|
|
268
634
|
var commands = /* @__PURE__ */ new Map();
|
|
269
635
|
function registerCommand(command) {
|
|
270
|
-
commands.set(command.name, command);
|
|
636
|
+
commands.set(command.name, { ...command, source: "core" });
|
|
637
|
+
}
|
|
638
|
+
function registerPluginCommand(command, pluginName) {
|
|
639
|
+
commands.set(command.name, { ...command, source: "plugin", pluginName });
|
|
640
|
+
}
|
|
641
|
+
function unregisterPluginCommands(pluginName) {
|
|
642
|
+
for (const [name, cmd] of commands.entries()) {
|
|
643
|
+
if (cmd.source === "plugin" && cmd.pluginName === pluginName) {
|
|
644
|
+
commands.delete(name);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
271
647
|
}
|
|
272
648
|
function getCommand(name) {
|
|
273
649
|
return commands.get(name);
|
|
@@ -275,14 +651,29 @@ function getCommand(name) {
|
|
|
275
651
|
function getAllCommands() {
|
|
276
652
|
return Array.from(commands.values());
|
|
277
653
|
}
|
|
654
|
+
function getPluginCommands() {
|
|
655
|
+
return Array.from(commands.values()).filter((cmd) => cmd.source === "plugin");
|
|
656
|
+
}
|
|
657
|
+
function checkCommandConflict(name) {
|
|
658
|
+
const existing = commands.get(name);
|
|
659
|
+
if (!existing) {
|
|
660
|
+
return { exists: false };
|
|
661
|
+
}
|
|
662
|
+
return {
|
|
663
|
+
exists: true,
|
|
664
|
+
source: existing.source,
|
|
665
|
+
pluginName: existing.pluginName
|
|
666
|
+
};
|
|
667
|
+
}
|
|
278
668
|
|
|
279
669
|
// src/commands/utilities/help.ts
|
|
280
670
|
var categories = {
|
|
281
671
|
"Utilities": ["help", "exit", "calc", "password", "stats", "time", "countdown", "timer", "todo", "history"],
|
|
282
|
-
"Fun": ["joke", "quote", "fortune", "dice", "flip", "pomodoro"],
|
|
672
|
+
"Fun": ["joke", "quote", "fortune", "dice", "flip", "pomodoro", "zammy"],
|
|
283
673
|
"Creative": ["asciiart", "figlet", "lorem", "color"],
|
|
284
674
|
"Dev": ["hash", "uuid", "encode"],
|
|
285
|
-
"Info": ["weather"]
|
|
675
|
+
"Info": ["weather"],
|
|
676
|
+
"System": ["plugin"]
|
|
286
677
|
};
|
|
287
678
|
registerCommand({
|
|
288
679
|
name: "help",
|
|
@@ -335,8 +726,24 @@ registerCommand({
|
|
|
335
726
|
}
|
|
336
727
|
console.log("");
|
|
337
728
|
}
|
|
729
|
+
const pluginCmds = getPluginCommands();
|
|
730
|
+
if (pluginCmds.length > 0) {
|
|
731
|
+
console.log(` ${symbols.gear} ${theme.b.secondary("Plugins")}`);
|
|
732
|
+
console.log(theme.dim(" " + "\u2500".repeat(46)));
|
|
733
|
+
for (const cmd of pluginCmds) {
|
|
734
|
+
const paddedName = cmd.name.padEnd(maxNameLength + 2);
|
|
735
|
+
const pluginBadge = cmd.pluginName ? theme.dim(`[${cmd.pluginName}]`) : "";
|
|
736
|
+
console.log(
|
|
737
|
+
` ${theme.command("/" + paddedName)} ${theme.dim("\u2502")} ${theme.dim(cmd.description)} ${pluginBadge}`
|
|
738
|
+
);
|
|
739
|
+
}
|
|
740
|
+
console.log("");
|
|
741
|
+
}
|
|
338
742
|
const categorizedNames = Object.values(categories).flat();
|
|
339
|
-
const
|
|
743
|
+
const pluginCmdNames = pluginCmds.map((c) => c.name);
|
|
744
|
+
const uncategorized = commands2.filter(
|
|
745
|
+
(c) => !categorizedNames.includes(c.name) && !pluginCmdNames.includes(c.name) && c.source === "core"
|
|
746
|
+
);
|
|
340
747
|
if (uncategorized.length > 0) {
|
|
341
748
|
console.log(` ${symbols.folder} ${theme.b.secondary("Other")}`);
|
|
342
749
|
console.log(theme.dim(" " + "\u2500".repeat(46)));
|
|
@@ -785,7 +1192,7 @@ registerCommand({
|
|
|
785
1192
|
console.log("");
|
|
786
1193
|
let remaining = totalSeconds;
|
|
787
1194
|
let spinnerIndex = 0;
|
|
788
|
-
return new Promise((
|
|
1195
|
+
return new Promise((resolve6) => {
|
|
789
1196
|
const interval = setInterval(() => {
|
|
790
1197
|
process.stdout.write("\r\x1B[K");
|
|
791
1198
|
if (remaining <= 0) {
|
|
@@ -793,7 +1200,7 @@ registerCommand({
|
|
|
793
1200
|
console.log(` ${symbols.sparkle} ${theme.success("TIME'S UP!")} ${symbols.sparkle}`);
|
|
794
1201
|
console.log("");
|
|
795
1202
|
process.stdout.write("\x07");
|
|
796
|
-
|
|
1203
|
+
resolve6();
|
|
797
1204
|
return;
|
|
798
1205
|
}
|
|
799
1206
|
const spinner = spinnerFrames[spinnerIndex % spinnerFrames.length];
|
|
@@ -1111,58 +1518,368 @@ registerCommand({
|
|
|
1111
1518
|
}
|
|
1112
1519
|
});
|
|
1113
1520
|
|
|
1521
|
+
// src/handlers/utilities/env.ts
|
|
1522
|
+
function getAllEnvVars() {
|
|
1523
|
+
return Object.entries(process.env).filter(([, value]) => value !== void 0).map(([name, value]) => ({ name, value })).sort((a, b3) => a.name.localeCompare(b3.name));
|
|
1524
|
+
}
|
|
1525
|
+
function getEnvVar(name) {
|
|
1526
|
+
const exactMatch = process.env[name];
|
|
1527
|
+
if (exactMatch !== void 0) return exactMatch;
|
|
1528
|
+
const upperName = name.toUpperCase();
|
|
1529
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
1530
|
+
if (key.toUpperCase() === upperName) {
|
|
1531
|
+
return value;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
return void 0;
|
|
1535
|
+
}
|
|
1536
|
+
function searchEnvVars(query) {
|
|
1537
|
+
const lowerQuery = query.toLowerCase();
|
|
1538
|
+
return getAllEnvVars().filter(
|
|
1539
|
+
(env) => env.name.toLowerCase().includes(lowerQuery) || env.value.toLowerCase().includes(lowerQuery)
|
|
1540
|
+
);
|
|
1541
|
+
}
|
|
1542
|
+
function getPathEntries() {
|
|
1543
|
+
const pathVar = process.env.PATH || process.env.Path || "";
|
|
1544
|
+
const separator = process.platform === "win32" ? ";" : ":";
|
|
1545
|
+
return pathVar.split(separator).filter(Boolean);
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
// src/commands/utilities/env.ts
|
|
1549
|
+
registerCommand({
|
|
1550
|
+
name: "env",
|
|
1551
|
+
description: "View environment variables",
|
|
1552
|
+
usage: "/env [name|search|path]",
|
|
1553
|
+
async execute(args2) {
|
|
1554
|
+
const action = args2[0];
|
|
1555
|
+
console.log("");
|
|
1556
|
+
if (!action) {
|
|
1557
|
+
const vars = getAllEnvVars();
|
|
1558
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("ENVIRONMENT VARIABLES")} ${theme.dim(`(${vars.length})`)}`);
|
|
1559
|
+
console.log("");
|
|
1560
|
+
for (const env of vars.slice(0, 30)) {
|
|
1561
|
+
const displayValue = env.value.length > 50 ? env.value.slice(0, 47) + "..." : env.value;
|
|
1562
|
+
console.log(` ${theme.primary(env.name.padEnd(20))} ${theme.dim("=")} ${displayValue}`);
|
|
1563
|
+
}
|
|
1564
|
+
if (vars.length > 30) {
|
|
1565
|
+
console.log("");
|
|
1566
|
+
console.log(` ${theme.dim(`... and ${vars.length - 30} more. Use /env search <query> to filter.`)}`);
|
|
1567
|
+
}
|
|
1568
|
+
console.log("");
|
|
1569
|
+
return;
|
|
1570
|
+
}
|
|
1571
|
+
if (action.toLowerCase() === "path") {
|
|
1572
|
+
const paths = getPathEntries();
|
|
1573
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("PATH ENTRIES")} ${theme.dim(`(${paths.length})`)}`);
|
|
1574
|
+
console.log("");
|
|
1575
|
+
paths.forEach((p, i) => {
|
|
1576
|
+
console.log(` ${theme.dim(`${(i + 1).toString().padStart(2)}.`)} ${theme.primary(p)}`);
|
|
1577
|
+
});
|
|
1578
|
+
console.log("");
|
|
1579
|
+
return;
|
|
1580
|
+
}
|
|
1581
|
+
if (action.toLowerCase() === "search" && args2[1]) {
|
|
1582
|
+
const query = args2.slice(1).join(" ");
|
|
1583
|
+
const results = searchEnvVars(query);
|
|
1584
|
+
console.log(` ${symbols.sparkle} ${theme.gradient(`SEARCH: "${query}"`)} ${theme.dim(`(${results.length} matches)`)}`);
|
|
1585
|
+
console.log("");
|
|
1586
|
+
if (results.length === 0) {
|
|
1587
|
+
console.log(` ${theme.dim("No matches found")}`);
|
|
1588
|
+
} else {
|
|
1589
|
+
for (const env of results) {
|
|
1590
|
+
const displayValue = env.value.length > 50 ? env.value.slice(0, 47) + "..." : env.value;
|
|
1591
|
+
console.log(` ${theme.primary(env.name.padEnd(20))} ${theme.dim("=")} ${displayValue}`);
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
console.log("");
|
|
1595
|
+
return;
|
|
1596
|
+
}
|
|
1597
|
+
const value = getEnvVar(action);
|
|
1598
|
+
if (value !== void 0) {
|
|
1599
|
+
console.log(` ${theme.primary(action)} ${theme.dim("=")}`);
|
|
1600
|
+
console.log("");
|
|
1601
|
+
if (value.includes(process.platform === "win32" ? ";" : ":") && value.length > 100) {
|
|
1602
|
+
const separator = process.platform === "win32" ? ";" : ":";
|
|
1603
|
+
const parts = value.split(separator);
|
|
1604
|
+
parts.forEach((p, i) => {
|
|
1605
|
+
console.log(` ${theme.dim(`${(i + 1).toString().padStart(2)}.`)} ${p}`);
|
|
1606
|
+
});
|
|
1607
|
+
} else {
|
|
1608
|
+
console.log(` ${theme.success(value)}`);
|
|
1609
|
+
}
|
|
1610
|
+
} else {
|
|
1611
|
+
console.log(` ${symbols.cross} ${theme.error(`Environment variable not found: ${action}`)}`);
|
|
1612
|
+
}
|
|
1613
|
+
console.log("");
|
|
1614
|
+
}
|
|
1615
|
+
});
|
|
1616
|
+
|
|
1617
|
+
// src/handlers/utilities/size.ts
|
|
1618
|
+
import { statSync, readdirSync, existsSync as existsSync3 } from "fs";
|
|
1619
|
+
import { join as join3, basename } from "path";
|
|
1620
|
+
function formatBytes2(bytes) {
|
|
1621
|
+
if (bytes === 0) return "0 B";
|
|
1622
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
1623
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
1624
|
+
const size = bytes / Math.pow(1024, i);
|
|
1625
|
+
return `${size.toFixed(i > 0 ? 1 : 0)} ${units[i]}`;
|
|
1626
|
+
}
|
|
1627
|
+
function getSize(path) {
|
|
1628
|
+
if (!existsSync3(path)) return null;
|
|
1629
|
+
try {
|
|
1630
|
+
const stats = statSync(path);
|
|
1631
|
+
if (stats.isFile()) {
|
|
1632
|
+
return {
|
|
1633
|
+
path,
|
|
1634
|
+
name: basename(path),
|
|
1635
|
+
size: stats.size,
|
|
1636
|
+
isDirectory: false
|
|
1637
|
+
};
|
|
1638
|
+
}
|
|
1639
|
+
if (stats.isDirectory()) {
|
|
1640
|
+
return getDirSize(path);
|
|
1641
|
+
}
|
|
1642
|
+
return null;
|
|
1643
|
+
} catch {
|
|
1644
|
+
return null;
|
|
1645
|
+
}
|
|
1646
|
+
}
|
|
1647
|
+
function getDirSize(dirPath) {
|
|
1648
|
+
const children = [];
|
|
1649
|
+
let totalSize = 0;
|
|
1650
|
+
try {
|
|
1651
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
1652
|
+
for (const entry of entries) {
|
|
1653
|
+
const entryPath = join3(dirPath, entry.name);
|
|
1654
|
+
try {
|
|
1655
|
+
if (entry.isFile()) {
|
|
1656
|
+
const stats = statSync(entryPath);
|
|
1657
|
+
totalSize += stats.size;
|
|
1658
|
+
children.push({
|
|
1659
|
+
path: entryPath,
|
|
1660
|
+
name: entry.name,
|
|
1661
|
+
size: stats.size,
|
|
1662
|
+
isDirectory: false
|
|
1663
|
+
});
|
|
1664
|
+
} else if (entry.isDirectory()) {
|
|
1665
|
+
if (["node_modules", ".git", "dist", "build", ".next", "coverage"].includes(entry.name)) {
|
|
1666
|
+
const subSize = getQuickDirSize(entryPath);
|
|
1667
|
+
totalSize += subSize;
|
|
1668
|
+
children.push({
|
|
1669
|
+
path: entryPath,
|
|
1670
|
+
name: entry.name,
|
|
1671
|
+
size: subSize,
|
|
1672
|
+
isDirectory: true
|
|
1673
|
+
});
|
|
1674
|
+
} else {
|
|
1675
|
+
const subInfo = getDirSize(entryPath);
|
|
1676
|
+
totalSize += subInfo.size;
|
|
1677
|
+
children.push(subInfo);
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
} catch {
|
|
1681
|
+
}
|
|
1682
|
+
}
|
|
1683
|
+
} catch {
|
|
1684
|
+
}
|
|
1685
|
+
children.sort((a, b3) => b3.size - a.size);
|
|
1686
|
+
return {
|
|
1687
|
+
path: dirPath,
|
|
1688
|
+
name: basename(dirPath),
|
|
1689
|
+
size: totalSize,
|
|
1690
|
+
isDirectory: true,
|
|
1691
|
+
children
|
|
1692
|
+
};
|
|
1693
|
+
}
|
|
1694
|
+
function getQuickDirSize(dirPath) {
|
|
1695
|
+
let totalSize = 0;
|
|
1696
|
+
try {
|
|
1697
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
1698
|
+
for (const entry of entries) {
|
|
1699
|
+
const entryPath = join3(dirPath, entry.name);
|
|
1700
|
+
try {
|
|
1701
|
+
if (entry.isFile()) {
|
|
1702
|
+
totalSize += statSync(entryPath).size;
|
|
1703
|
+
} else if (entry.isDirectory()) {
|
|
1704
|
+
totalSize += getQuickDirSize(entryPath);
|
|
1705
|
+
}
|
|
1706
|
+
} catch {
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1709
|
+
} catch {
|
|
1710
|
+
}
|
|
1711
|
+
return totalSize;
|
|
1712
|
+
}
|
|
1713
|
+
function findLargestFiles(dirPath, count = 10) {
|
|
1714
|
+
const allFiles = [];
|
|
1715
|
+
function collectFiles(dir) {
|
|
1716
|
+
try {
|
|
1717
|
+
const entries = readdirSync(dir, { withFileTypes: true });
|
|
1718
|
+
for (const entry of entries) {
|
|
1719
|
+
const entryPath = join3(dir, entry.name);
|
|
1720
|
+
try {
|
|
1721
|
+
if (entry.isFile()) {
|
|
1722
|
+
const stats = statSync(entryPath);
|
|
1723
|
+
allFiles.push({
|
|
1724
|
+
path: entryPath,
|
|
1725
|
+
name: entry.name,
|
|
1726
|
+
size: stats.size,
|
|
1727
|
+
isDirectory: false
|
|
1728
|
+
});
|
|
1729
|
+
} else if (entry.isDirectory()) {
|
|
1730
|
+
if (!["node_modules", ".git"].includes(entry.name)) {
|
|
1731
|
+
collectFiles(entryPath);
|
|
1732
|
+
}
|
|
1733
|
+
}
|
|
1734
|
+
} catch {
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
} catch {
|
|
1738
|
+
}
|
|
1739
|
+
}
|
|
1740
|
+
collectFiles(dirPath);
|
|
1741
|
+
allFiles.sort((a, b3) => b3.size - a.size);
|
|
1742
|
+
return allFiles.slice(0, count);
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
// src/commands/utilities/size.ts
|
|
1746
|
+
import { resolve } from "path";
|
|
1747
|
+
registerCommand({
|
|
1748
|
+
name: "size",
|
|
1749
|
+
description: "Analyze file/folder sizes",
|
|
1750
|
+
usage: "/size [path] [--top N]",
|
|
1751
|
+
async execute(args2) {
|
|
1752
|
+
const targetPath = args2[0] || ".";
|
|
1753
|
+
const absPath = resolve(targetPath);
|
|
1754
|
+
const topIndex = args2.indexOf("--top");
|
|
1755
|
+
const showTop = topIndex !== -1 ? parseInt(args2[topIndex + 1]) || 10 : 0;
|
|
1756
|
+
console.log("");
|
|
1757
|
+
if (showTop > 0) {
|
|
1758
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("LARGEST FILES")}`);
|
|
1759
|
+
console.log(` ${theme.dim(`in ${absPath}`)}`);
|
|
1760
|
+
console.log("");
|
|
1761
|
+
const largestFiles = findLargestFiles(absPath, showTop);
|
|
1762
|
+
if (largestFiles.length === 0) {
|
|
1763
|
+
console.log(` ${theme.dim("No files found")}`);
|
|
1764
|
+
} else {
|
|
1765
|
+
const maxSize = largestFiles[0].size;
|
|
1766
|
+
for (let i = 0; i < largestFiles.length; i++) {
|
|
1767
|
+
const file = largestFiles[i];
|
|
1768
|
+
const percent = file.size / maxSize * 100;
|
|
1769
|
+
const bar = progressBar(percent, 20);
|
|
1770
|
+
const relativePath = file.path.replace(absPath, ".").replace(/\\/g, "/");
|
|
1771
|
+
console.log(
|
|
1772
|
+
` ${theme.dim(`${(i + 1).toString().padStart(2)}.`)} ${bar} ${theme.primary(formatBytes2(file.size).padStart(10))} ${relativePath}`
|
|
1773
|
+
);
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
console.log("");
|
|
1777
|
+
return;
|
|
1778
|
+
}
|
|
1779
|
+
const info = getSize(absPath);
|
|
1780
|
+
if (!info) {
|
|
1781
|
+
console.log(` ${symbols.cross} ${theme.error(`Path not found: ${absPath}`)}`);
|
|
1782
|
+
console.log("");
|
|
1783
|
+
return;
|
|
1784
|
+
}
|
|
1785
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("SIZE ANALYSIS")}`);
|
|
1786
|
+
console.log(` ${theme.dim(absPath)}`);
|
|
1787
|
+
console.log("");
|
|
1788
|
+
if (!info.isDirectory) {
|
|
1789
|
+
console.log(` ${theme.primary(info.name)}: ${theme.success(formatBytes2(info.size))}`);
|
|
1790
|
+
} else {
|
|
1791
|
+
console.log(` ${theme.secondary("Total:")} ${theme.success(formatBytes2(info.size))}`);
|
|
1792
|
+
console.log("");
|
|
1793
|
+
if (info.children && info.children.length > 0) {
|
|
1794
|
+
const maxSize = info.children[0].size;
|
|
1795
|
+
const displayCount = Math.min(15, info.children.length);
|
|
1796
|
+
for (let i = 0; i < displayCount; i++) {
|
|
1797
|
+
const child = info.children[i];
|
|
1798
|
+
const percent = child.size / maxSize * 100;
|
|
1799
|
+
const bar = progressBar(percent, 15);
|
|
1800
|
+
const icon = child.isDirectory ? symbols.folder : symbols.bullet;
|
|
1801
|
+
console.log(
|
|
1802
|
+
` ${bar} ${theme.primary(formatBytes2(child.size).padStart(10))} ${icon} ${child.name}`
|
|
1803
|
+
);
|
|
1804
|
+
}
|
|
1805
|
+
if (info.children.length > displayCount) {
|
|
1806
|
+
console.log("");
|
|
1807
|
+
console.log(` ${theme.dim(`... and ${info.children.length - displayCount} more items`)}`);
|
|
1808
|
+
}
|
|
1809
|
+
} else {
|
|
1810
|
+
console.log(` ${theme.dim("Directory is empty")}`);
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
console.log("");
|
|
1814
|
+
console.log(` ${theme.dim("Tip: /size . --top 10 shows largest files")}`);
|
|
1815
|
+
console.log("");
|
|
1816
|
+
}
|
|
1817
|
+
});
|
|
1818
|
+
|
|
1114
1819
|
// src/commands/fun/joke.ts
|
|
1115
1820
|
var fallbackJokes = [
|
|
1116
1821
|
{ setup: "Why do programmers prefer dark mode?", punchline: "Because light attracts bugs!" },
|
|
1117
1822
|
{ setup: "Why do Java developers wear glasses?", punchline: "Because they can't C#!" },
|
|
1118
1823
|
{ setup: "A SQL query walks into a bar, walks up to two tables and asks...", punchline: "Can I join you?" },
|
|
1119
1824
|
{ setup: "Why did the developer go broke?", punchline: "Because he used up all his cache!" },
|
|
1120
|
-
{ setup: "How many programmers does it take to change a light bulb?", punchline: "None, that's a hardware problem!" }
|
|
1825
|
+
{ setup: "How many programmers does it take to change a light bulb?", punchline: "None, that's a hardware problem!" },
|
|
1826
|
+
{ setup: "Why do programmers hate nature?", punchline: "It has too many bugs!" },
|
|
1827
|
+
{ setup: "What's a programmer's favorite hangout place?", punchline: "Foo Bar!" },
|
|
1828
|
+
{ setup: "Why was the JavaScript developer sad?", punchline: "Because he didn't Node how to Express himself!" },
|
|
1829
|
+
{ setup: "What do you call a computer that sings?", punchline: "A-Dell!" },
|
|
1830
|
+
{ setup: "Why did the developer quit his job?", punchline: "Because he didn't get arrays (a raise)!" }
|
|
1121
1831
|
];
|
|
1832
|
+
async function fetchJoke() {
|
|
1833
|
+
try {
|
|
1834
|
+
const response = await fetch("https://v2.jokeapi.dev/joke/Programming?type=twopart&safe-mode", {
|
|
1835
|
+
signal: AbortSignal.timeout(3e3)
|
|
1836
|
+
});
|
|
1837
|
+
if (response.ok) {
|
|
1838
|
+
const data = await response.json();
|
|
1839
|
+
if (!data.error && data.setup && data.delivery) {
|
|
1840
|
+
return { setup: data.setup, punchline: data.delivery };
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
} catch {
|
|
1844
|
+
}
|
|
1845
|
+
try {
|
|
1846
|
+
const response = await fetch("https://official-joke-api.appspot.com/jokes/programming/random", {
|
|
1847
|
+
signal: AbortSignal.timeout(3e3)
|
|
1848
|
+
});
|
|
1849
|
+
if (response.ok) {
|
|
1850
|
+
const data = await response.json();
|
|
1851
|
+
if (data && data[0]) {
|
|
1852
|
+
return { setup: data[0].setup, punchline: data[0].punchline };
|
|
1853
|
+
}
|
|
1854
|
+
}
|
|
1855
|
+
} catch {
|
|
1856
|
+
}
|
|
1857
|
+
return fallbackJokes[Math.floor(Math.random() * fallbackJokes.length)];
|
|
1858
|
+
}
|
|
1122
1859
|
registerCommand({
|
|
1123
1860
|
name: "joke",
|
|
1124
|
-
description: "Get a random joke",
|
|
1861
|
+
description: "Get a random programming joke",
|
|
1125
1862
|
usage: "/joke",
|
|
1126
1863
|
async execute(_args) {
|
|
1127
1864
|
console.log("");
|
|
1128
1865
|
console.log(` ${symbols.dice} ${theme.sunset("Getting a joke...")}`);
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
console.log(bubble.say(joke.setup, 55));
|
|
1142
|
-
console.log(` ${symbols.sparkle}`);
|
|
1143
|
-
await new Promise((resolve3) => setTimeout(resolve3, 1500));
|
|
1144
|
-
console.log("");
|
|
1145
|
-
console.log(` ${theme.gold(" \u2726")} ${theme.b.success(joke.punchline)} ${theme.gold("\u2726")}`);
|
|
1146
|
-
console.log("");
|
|
1147
|
-
console.log(` ${theme.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`);
|
|
1148
|
-
console.log(` ${symbols.dice} ${theme.dim("Run /joke again for another!")}`);
|
|
1149
|
-
console.log("");
|
|
1150
|
-
} catch (error) {
|
|
1151
|
-
process.stdout.write("\x1B[1A\x1B[2K");
|
|
1152
|
-
const joke = fallbackJokes[Math.floor(Math.random() * fallbackJokes.length)];
|
|
1153
|
-
console.log("");
|
|
1154
|
-
console.log(bubble.say(joke.setup, 55));
|
|
1155
|
-
console.log(` ${symbols.sparkle}`);
|
|
1156
|
-
await new Promise((resolve3) => setTimeout(resolve3, 1500));
|
|
1157
|
-
console.log("");
|
|
1158
|
-
console.log(` ${theme.gold(" \u2726")} ${theme.b.success(joke.punchline)} ${theme.gold("\u2726")}`);
|
|
1159
|
-
console.log("");
|
|
1160
|
-
}
|
|
1866
|
+
const joke = await fetchJoke();
|
|
1867
|
+
process.stdout.write("\x1B[1A\x1B[2K");
|
|
1868
|
+
console.log("");
|
|
1869
|
+
console.log(bubble.say(joke.setup, 55));
|
|
1870
|
+
console.log(` ${symbols.sparkle}`);
|
|
1871
|
+
await new Promise((resolve6) => setTimeout(resolve6, 1500));
|
|
1872
|
+
console.log("");
|
|
1873
|
+
console.log(` ${theme.gold(" \u2726")} ${theme.b.success(joke.punchline)} ${theme.gold("\u2726")}`);
|
|
1874
|
+
console.log("");
|
|
1875
|
+
console.log(` ${theme.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500")}`);
|
|
1876
|
+
console.log(` ${symbols.dice} ${theme.dim("Run /joke again for another!")}`);
|
|
1877
|
+
console.log("");
|
|
1161
1878
|
}
|
|
1162
1879
|
});
|
|
1163
1880
|
|
|
1164
1881
|
// src/commands/fun/quote.ts
|
|
1165
|
-
var
|
|
1882
|
+
var fallbackQuotes = [
|
|
1166
1883
|
{ text: "The only way to do great work is to love what you do.", author: "Steve Jobs" },
|
|
1167
1884
|
{ text: "Code is like humor. When you have to explain it, it's bad.", author: "Cory House" },
|
|
1168
1885
|
{ text: "First, solve the problem. Then, write the code.", author: "John Johnson" },
|
|
@@ -1179,6 +1896,33 @@ var quotes = [
|
|
|
1179
1896
|
{ text: "There are only two hard things in Computer Science: cache invalidation and naming things.", author: "Phil Karlton" },
|
|
1180
1897
|
{ text: "The best time to plant a tree was 20 years ago. The second best time is now.", author: "Chinese Proverb" }
|
|
1181
1898
|
];
|
|
1899
|
+
async function fetchQuote() {
|
|
1900
|
+
try {
|
|
1901
|
+
const response = await fetch("https://zenquotes.io/api/random", {
|
|
1902
|
+
signal: AbortSignal.timeout(3e3)
|
|
1903
|
+
});
|
|
1904
|
+
if (response.ok) {
|
|
1905
|
+
const data = await response.json();
|
|
1906
|
+
if (data && data[0]) {
|
|
1907
|
+
return { text: data[0].q, author: data[0].a };
|
|
1908
|
+
}
|
|
1909
|
+
}
|
|
1910
|
+
} catch {
|
|
1911
|
+
}
|
|
1912
|
+
try {
|
|
1913
|
+
const response = await fetch("https://api.quotable.io/random", {
|
|
1914
|
+
signal: AbortSignal.timeout(3e3)
|
|
1915
|
+
});
|
|
1916
|
+
if (response.ok) {
|
|
1917
|
+
const data = await response.json();
|
|
1918
|
+
if (data && data.content) {
|
|
1919
|
+
return { text: data.content, author: data.author };
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
} catch {
|
|
1923
|
+
}
|
|
1924
|
+
return fallbackQuotes[Math.floor(Math.random() * fallbackQuotes.length)];
|
|
1925
|
+
}
|
|
1182
1926
|
function wrapText(text, maxWidth) {
|
|
1183
1927
|
const words = text.split(" ");
|
|
1184
1928
|
const lines = [];
|
|
@@ -1199,7 +1943,10 @@ registerCommand({
|
|
|
1199
1943
|
description: "Get an inspirational quote",
|
|
1200
1944
|
usage: "/quote",
|
|
1201
1945
|
async execute(_args) {
|
|
1202
|
-
|
|
1946
|
+
console.log("");
|
|
1947
|
+
console.log(` ${symbols.sparkle} ${theme.dim("Fetching wisdom...")}`);
|
|
1948
|
+
const quote = await fetchQuote();
|
|
1949
|
+
process.stdout.write("\x1B[1A\x1B[2K\x1B[1A\x1B[2K");
|
|
1203
1950
|
const wrapped = wrapText(quote.text, 55);
|
|
1204
1951
|
console.log("");
|
|
1205
1952
|
console.log(` ${theme.dim("\u256D\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u256E")}`);
|
|
@@ -1405,7 +2152,7 @@ function rollDice(count = 1, sides = 6) {
|
|
|
1405
2152
|
count: safeCount,
|
|
1406
2153
|
sides: safeSides,
|
|
1407
2154
|
rolls,
|
|
1408
|
-
total: rolls.reduce((a,
|
|
2155
|
+
total: rolls.reduce((a, b3) => a + b3, 0),
|
|
1409
2156
|
isStandardD6: safeSides === 6 && safeCount <= 3
|
|
1410
2157
|
};
|
|
1411
2158
|
}
|
|
@@ -1655,18 +2402,110 @@ registerCommand({
|
|
|
1655
2402
|
}
|
|
1656
2403
|
});
|
|
1657
2404
|
|
|
1658
|
-
// src/commands/
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
2405
|
+
// src/commands/fun/zammy.ts
|
|
2406
|
+
var MIN_WIDTH_FOR_MASCOT3 = 50;
|
|
2407
|
+
var MIN_WIDTH_FOR_ASCII = 70;
|
|
2408
|
+
var asciiMascot = `
|
|
2409
|
+
**/***********,
|
|
2410
|
+
****** **//////////****
|
|
2411
|
+
***/**///////////////////////**
|
|
2412
|
+
****///////////**////////////////(**
|
|
2413
|
+
***////& &////////////% &////* ,////(**
|
|
2414
|
+
*//////&&&/////////////&&&//(/////((((((*
|
|
2415
|
+
**/////////&&&&&&&&&&&&////(//((/(((((((((/*
|
|
2416
|
+
**/////////&&&&&&/****&&//((* /(((((((((((((*
|
|
2417
|
+
,*////* *///&&&*******&*/(((((((((((((((((((**
|
|
2418
|
+
**////////////*//(**(/(/((((((((((((((((/(((*
|
|
2419
|
+
**//////////////(((/(((((((((/ .*((((((((((**
|
|
2420
|
+
**////////(/((((/((((((((((((((((((((((((/*
|
|
2421
|
+
**(((///(((((//((((((((((((((((((((((#**
|
|
2422
|
+
**/((((((((((((((((/*(((((((((((((*
|
|
2423
|
+
***((((((((((((((((((#(((((** */*///
|
|
2424
|
+
***************** *(((( .*/
|
|
2425
|
+
`;
|
|
2426
|
+
registerCommand({
|
|
2427
|
+
name: "zammy",
|
|
2428
|
+
description: "Say hi to Zammy the slime!",
|
|
2429
|
+
usage: "/zammy [mood|ascii|moods]",
|
|
2430
|
+
async execute(args2) {
|
|
2431
|
+
const mood = args2[0] || "happy";
|
|
2432
|
+
const termWidth = process.stdout.columns || 80;
|
|
2433
|
+
if (mood === "ascii" || mood === "mascot") {
|
|
2434
|
+
console.log("");
|
|
2435
|
+
if (termWidth < MIN_WIDTH_FOR_ASCII) {
|
|
2436
|
+
console.log(` ${miniSlime.happy} Hi! I'm Zammy!`);
|
|
2437
|
+
console.log(` ${theme.dim("(widen your terminal to see my full form!)")}`);
|
|
2438
|
+
} else {
|
|
2439
|
+
console.log(theme.primary(asciiMascot));
|
|
2440
|
+
}
|
|
2441
|
+
console.log(` ${theme.secondary(`"${getGreeting()}"`)}`);
|
|
2442
|
+
console.log("");
|
|
2443
|
+
return;
|
|
2444
|
+
}
|
|
2445
|
+
if (mood === "moods" || mood === "all") {
|
|
2446
|
+
console.log("");
|
|
2447
|
+
console.log(` ${symbols.sparkle} ${theme.primary("Zammy's moods:")} ${symbols.sparkle}`);
|
|
2448
|
+
console.log("");
|
|
2449
|
+
if (termWidth < MIN_WIDTH_FOR_MASCOT3) {
|
|
2450
|
+
for (const m of validMoods) {
|
|
2451
|
+
const mini = miniSlime[m];
|
|
2452
|
+
if (mini) {
|
|
2453
|
+
console.log(` ${mini} ${theme.dim(m)}`);
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2456
|
+
console.log("");
|
|
2457
|
+
console.log(` ${theme.dim("(widen your terminal to see full mood sprites!)")}`);
|
|
2458
|
+
} else {
|
|
2459
|
+
for (const m of validMoods) {
|
|
2460
|
+
const frameFunc2 = slimeFrames[m];
|
|
2461
|
+
if (frameFunc2) {
|
|
2462
|
+
console.log(theme.secondary(` \u2500\u2500\u2500 ${m.toUpperCase()} \u2500\u2500\u2500`));
|
|
2463
|
+
const art = frameFunc2();
|
|
2464
|
+
art.forEach((line) => console.log(" " + line));
|
|
2465
|
+
console.log("");
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
}
|
|
2469
|
+
return;
|
|
2470
|
+
}
|
|
2471
|
+
if (!validMoods.includes(mood)) {
|
|
2472
|
+
console.log("");
|
|
2473
|
+
console.log(` ${miniSlime.surprised} ${theme.warning("I don't know that mood!")}`);
|
|
2474
|
+
console.log(` ${theme.dim(`Try: ${validMoods.join(", ")}, ascii`)}`);
|
|
2475
|
+
console.log(` ${theme.dim(`Or: /zammy moods`)}`);
|
|
2476
|
+
console.log("");
|
|
2477
|
+
return;
|
|
2478
|
+
}
|
|
2479
|
+
const frameFunc = slimeFrames[mood];
|
|
2480
|
+
const greeting = getGreeting();
|
|
2481
|
+
console.log("");
|
|
2482
|
+
if (termWidth < MIN_WIDTH_FOR_MASCOT3) {
|
|
2483
|
+
const mini = miniSlime[mood] || miniSlime.happy;
|
|
2484
|
+
console.log(` ${mini}`);
|
|
2485
|
+
} else if (frameFunc) {
|
|
2486
|
+
const art = frameFunc();
|
|
2487
|
+
art.forEach((line) => console.log(" " + line));
|
|
2488
|
+
}
|
|
2489
|
+
console.log("");
|
|
2490
|
+
console.log(` ${theme.secondary(`"${greeting}"`)}`);
|
|
2491
|
+
console.log("");
|
|
2492
|
+
console.log(` ${theme.dim("I'm Zammy, your CLI buddy! Type /help to see what I can do~")}`);
|
|
2493
|
+
console.log("");
|
|
2494
|
+
}
|
|
2495
|
+
});
|
|
2496
|
+
|
|
2497
|
+
// src/commands/creative/asciiart.ts
|
|
2498
|
+
import Jimp from "jimp";
|
|
2499
|
+
import { existsSync as existsSync4 } from "fs";
|
|
2500
|
+
import { resolve as resolve2 } from "path";
|
|
2501
|
+
var CHAR_RAMPS = {
|
|
2502
|
+
// Standard - 10 levels, balanced
|
|
2503
|
+
standard: " .:-=+*#%@",
|
|
2504
|
+
// Detailed - 16 levels, more gradation
|
|
2505
|
+
detailed: " .'`^\",:;Il!i><~+_-?][}{1)(|/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$",
|
|
2506
|
+
// Block characters - uses Unicode block elements for smoother look
|
|
2507
|
+
blocks: " \u2591\u2592\u2593\u2588",
|
|
2508
|
+
// Simple - 5 levels for cleaner output
|
|
1670
2509
|
simple: " .:\u2591\u2588",
|
|
1671
2510
|
// Extended - Maximum depth with 70 characters
|
|
1672
2511
|
extended: "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\\|()1{}[]?-_+~<>i!lI;:,\"^`'. "
|
|
@@ -1682,8 +2521,8 @@ var SOBEL_Y = [
|
|
|
1682
2521
|
[1, 2, 1]
|
|
1683
2522
|
];
|
|
1684
2523
|
function getAsciiChar(brightness, ramp, inverted) {
|
|
1685
|
-
const
|
|
1686
|
-
const index = Math.floor(
|
|
2524
|
+
const b3 = Math.max(0, Math.min(255, brightness));
|
|
2525
|
+
const index = Math.floor(b3 / 255 * (ramp.length - 1));
|
|
1687
2526
|
return inverted ? ramp[ramp.length - 1 - index] : ramp[index];
|
|
1688
2527
|
}
|
|
1689
2528
|
async function applyEdgeEnhancement(image, strength) {
|
|
@@ -1763,8 +2602,8 @@ registerCommand({
|
|
|
1763
2602
|
i++;
|
|
1764
2603
|
}
|
|
1765
2604
|
}
|
|
1766
|
-
const fullPath =
|
|
1767
|
-
if (!
|
|
2605
|
+
const fullPath = resolve2(process.cwd(), imagePath);
|
|
2606
|
+
if (!existsSync4(fullPath)) {
|
|
1768
2607
|
console.log(theme.error(`File not found: ${imagePath}`));
|
|
1769
2608
|
return;
|
|
1770
2609
|
}
|
|
@@ -2045,7 +2884,7 @@ registerCommand({
|
|
|
2045
2884
|
});
|
|
2046
2885
|
|
|
2047
2886
|
// src/commands/creative/color.ts
|
|
2048
|
-
import
|
|
2887
|
+
import chalk4 from "chalk";
|
|
2049
2888
|
|
|
2050
2889
|
// src/handlers/creative/color.ts
|
|
2051
2890
|
function hexToRgb(hex) {
|
|
@@ -2056,14 +2895,14 @@ function hexToRgb(hex) {
|
|
|
2056
2895
|
b: parseInt(result[3], 16)
|
|
2057
2896
|
} : null;
|
|
2058
2897
|
}
|
|
2059
|
-
function rgbToHex(r, g,
|
|
2060
|
-
return "#" + [r, g,
|
|
2898
|
+
function rgbToHex(r, g, b3) {
|
|
2899
|
+
return "#" + [r, g, b3].map((x) => x.toString(16).padStart(2, "0")).join("");
|
|
2061
2900
|
}
|
|
2062
|
-
function rgbToHsl(r, g,
|
|
2901
|
+
function rgbToHsl(r, g, b3) {
|
|
2063
2902
|
r /= 255;
|
|
2064
2903
|
g /= 255;
|
|
2065
|
-
|
|
2066
|
-
const max = Math.max(r, g,
|
|
2904
|
+
b3 /= 255;
|
|
2905
|
+
const max = Math.max(r, g, b3), min = Math.min(r, g, b3);
|
|
2067
2906
|
let h = 0, s = 0;
|
|
2068
2907
|
const l = (max + min) / 2;
|
|
2069
2908
|
if (max !== min) {
|
|
@@ -2071,20 +2910,20 @@ function rgbToHsl(r, g, b) {
|
|
|
2071
2910
|
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
|
2072
2911
|
switch (max) {
|
|
2073
2912
|
case r:
|
|
2074
|
-
h = ((g -
|
|
2913
|
+
h = ((g - b3) / d + (g < b3 ? 6 : 0)) / 6;
|
|
2075
2914
|
break;
|
|
2076
2915
|
case g:
|
|
2077
|
-
h = ((
|
|
2916
|
+
h = ((b3 - r) / d + 2) / 6;
|
|
2078
2917
|
break;
|
|
2079
|
-
case
|
|
2918
|
+
case b3:
|
|
2080
2919
|
h = ((r - g) / d + 4) / 6;
|
|
2081
2920
|
break;
|
|
2082
2921
|
}
|
|
2083
2922
|
}
|
|
2084
2923
|
return { h: Math.round(h * 360), s: Math.round(s * 100), l: Math.round(l * 100) };
|
|
2085
2924
|
}
|
|
2086
|
-
function getLuminance(r, g,
|
|
2087
|
-
return (0.299 * r + 0.587 * g + 0.114 *
|
|
2925
|
+
function getLuminance(r, g, b3) {
|
|
2926
|
+
return (0.299 * r + 0.587 * g + 0.114 * b3) / 255;
|
|
2088
2927
|
}
|
|
2089
2928
|
function randomColor() {
|
|
2090
2929
|
return {
|
|
@@ -2094,12 +2933,12 @@ function randomColor() {
|
|
|
2094
2933
|
};
|
|
2095
2934
|
}
|
|
2096
2935
|
function parseColor(input) {
|
|
2097
|
-
let r, g,
|
|
2936
|
+
let r, g, b3;
|
|
2098
2937
|
if (!input || input === "random") {
|
|
2099
2938
|
const rgb = randomColor();
|
|
2100
2939
|
r = rgb.r;
|
|
2101
2940
|
g = rgb.g;
|
|
2102
|
-
|
|
2941
|
+
b3 = rgb.b;
|
|
2103
2942
|
} else {
|
|
2104
2943
|
const hexMatch = input.match(/^#?([a-f\d]{6}|[a-f\d]{3})$/i);
|
|
2105
2944
|
if (hexMatch) {
|
|
@@ -2111,7 +2950,7 @@ function parseColor(input) {
|
|
|
2111
2950
|
if (rgb) {
|
|
2112
2951
|
r = rgb.r;
|
|
2113
2952
|
g = rgb.g;
|
|
2114
|
-
|
|
2953
|
+
b3 = rgb.b;
|
|
2115
2954
|
} else {
|
|
2116
2955
|
return { error: "Invalid hex color" };
|
|
2117
2956
|
}
|
|
@@ -2120,24 +2959,24 @@ function parseColor(input) {
|
|
|
2120
2959
|
if (rgbMatch) {
|
|
2121
2960
|
r = Math.min(255, parseInt(rgbMatch[1]));
|
|
2122
2961
|
g = Math.min(255, parseInt(rgbMatch[2]));
|
|
2123
|
-
|
|
2962
|
+
b3 = Math.min(255, parseInt(rgbMatch[3]));
|
|
2124
2963
|
} else {
|
|
2125
2964
|
const nums = input.split(/[\s,]+/).map((n) => parseInt(n)).filter((n) => !isNaN(n));
|
|
2126
2965
|
if (nums.length === 3) {
|
|
2127
2966
|
r = Math.min(255, nums[0]);
|
|
2128
2967
|
g = Math.min(255, nums[1]);
|
|
2129
|
-
|
|
2968
|
+
b3 = Math.min(255, nums[2]);
|
|
2130
2969
|
} else {
|
|
2131
2970
|
return { error: "Could not parse color" };
|
|
2132
2971
|
}
|
|
2133
2972
|
}
|
|
2134
2973
|
}
|
|
2135
2974
|
}
|
|
2136
|
-
const hex = rgbToHex(r, g,
|
|
2137
|
-
const hsl = rgbToHsl(r, g,
|
|
2138
|
-
const luminance = getLuminance(r, g,
|
|
2975
|
+
const hex = rgbToHex(r, g, b3);
|
|
2976
|
+
const hsl = rgbToHsl(r, g, b3);
|
|
2977
|
+
const luminance = getLuminance(r, g, b3);
|
|
2139
2978
|
return {
|
|
2140
|
-
rgb: { r, g, b },
|
|
2979
|
+
rgb: { r, g, b: b3 },
|
|
2141
2980
|
hex,
|
|
2142
2981
|
hsl,
|
|
2143
2982
|
luminance,
|
|
@@ -2194,7 +3033,7 @@ registerCommand({
|
|
|
2194
3033
|
console.log("");
|
|
2195
3034
|
console.log(` ${symbols.palette} ${theme.gradient("COLOR CONVERTER")} ${symbols.palette}`);
|
|
2196
3035
|
console.log("");
|
|
2197
|
-
const colorBlock =
|
|
3036
|
+
const colorBlock = chalk4.bgHex(hex).hex(textColor);
|
|
2198
3037
|
console.log(` ${colorBlock(" ")}`);
|
|
2199
3038
|
console.log(` ${colorBlock(" ")}`);
|
|
2200
3039
|
console.log(` ${colorBlock(` ${hex.toUpperCase()} `.slice(0, 40))}`);
|
|
@@ -2209,14 +3048,14 @@ registerCommand({
|
|
|
2209
3048
|
let shadesLine = " ";
|
|
2210
3049
|
const shades = generateShades(rgb);
|
|
2211
3050
|
for (const shade of shades) {
|
|
2212
|
-
shadesLine +=
|
|
3051
|
+
shadesLine += chalk4.bgRgb(shade.r, shade.g, shade.b)(" ");
|
|
2213
3052
|
}
|
|
2214
3053
|
console.log(shadesLine);
|
|
2215
3054
|
console.log(` ${theme.dim("Tints:")}`);
|
|
2216
3055
|
let tintsLine = " ";
|
|
2217
3056
|
const tints = generateTints(rgb);
|
|
2218
3057
|
for (const tint of tints) {
|
|
2219
|
-
tintsLine +=
|
|
3058
|
+
tintsLine += chalk4.bgRgb(tint.r, tint.g, tint.b)(" ");
|
|
2220
3059
|
}
|
|
2221
3060
|
console.log(tintsLine);
|
|
2222
3061
|
console.log("");
|
|
@@ -2280,189 +3119,2016 @@ registerCommand({
|
|
|
2280
3119
|
], 70));
|
|
2281
3120
|
console.log("");
|
|
2282
3121
|
}
|
|
2283
|
-
});
|
|
3122
|
+
});
|
|
3123
|
+
|
|
3124
|
+
// src/handlers/dev/uuid.ts
|
|
3125
|
+
import { randomUUID } from "crypto";
|
|
3126
|
+
function generateUuids(count = 1) {
|
|
3127
|
+
const safeCount = Math.min(Math.max(1, count), 10);
|
|
3128
|
+
const uuids = [];
|
|
3129
|
+
for (let i = 0; i < safeCount; i++) {
|
|
3130
|
+
uuids.push(randomUUID());
|
|
3131
|
+
}
|
|
3132
|
+
return {
|
|
3133
|
+
uuids,
|
|
3134
|
+
count: safeCount
|
|
3135
|
+
};
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
// src/commands/dev/uuid.ts
|
|
3139
|
+
registerCommand({
|
|
3140
|
+
name: "uuid",
|
|
3141
|
+
description: "Generate UUID(s)",
|
|
3142
|
+
usage: "/uuid [count]",
|
|
3143
|
+
async execute(args2) {
|
|
3144
|
+
const count = parseInt(args2[0]) || 1;
|
|
3145
|
+
const result = generateUuids(count);
|
|
3146
|
+
console.log("");
|
|
3147
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("UUID GENERATOR")} ${symbols.sparkle}`);
|
|
3148
|
+
console.log("");
|
|
3149
|
+
result.uuids.forEach((uuid, i) => {
|
|
3150
|
+
console.log(` ${theme.dim(`${i + 1}.`)} ${theme.primary(uuid)}`);
|
|
3151
|
+
});
|
|
3152
|
+
console.log("");
|
|
3153
|
+
if (result.count === 1) {
|
|
3154
|
+
console.log(theme.dim(" Tip: /uuid 5 generates 5 UUIDs"));
|
|
3155
|
+
}
|
|
3156
|
+
console.log("");
|
|
3157
|
+
}
|
|
3158
|
+
});
|
|
3159
|
+
|
|
3160
|
+
// src/handlers/dev/encode.ts
|
|
3161
|
+
var SUPPORTED_METHODS = ["base64", "url", "hex"];
|
|
3162
|
+
function isValidMethod(method) {
|
|
3163
|
+
return SUPPORTED_METHODS.includes(method.toLowerCase());
|
|
3164
|
+
}
|
|
3165
|
+
function encodeText(text, method, direction) {
|
|
3166
|
+
let output;
|
|
3167
|
+
if (direction === "encode") {
|
|
3168
|
+
switch (method) {
|
|
3169
|
+
case "base64":
|
|
3170
|
+
output = Buffer.from(text).toString("base64");
|
|
3171
|
+
break;
|
|
3172
|
+
case "url":
|
|
3173
|
+
output = encodeURIComponent(text);
|
|
3174
|
+
break;
|
|
3175
|
+
case "hex":
|
|
3176
|
+
output = Buffer.from(text).toString("hex");
|
|
3177
|
+
break;
|
|
3178
|
+
}
|
|
3179
|
+
} else {
|
|
3180
|
+
switch (method) {
|
|
3181
|
+
case "base64":
|
|
3182
|
+
output = Buffer.from(text, "base64").toString("utf-8");
|
|
3183
|
+
break;
|
|
3184
|
+
case "url":
|
|
3185
|
+
output = decodeURIComponent(text);
|
|
3186
|
+
break;
|
|
3187
|
+
case "hex":
|
|
3188
|
+
output = Buffer.from(text, "hex").toString("utf-8");
|
|
3189
|
+
break;
|
|
3190
|
+
}
|
|
3191
|
+
}
|
|
3192
|
+
return {
|
|
3193
|
+
method: method.toUpperCase(),
|
|
3194
|
+
direction,
|
|
3195
|
+
input: text,
|
|
3196
|
+
output
|
|
3197
|
+
};
|
|
3198
|
+
}
|
|
3199
|
+
|
|
3200
|
+
// src/commands/dev/encode.ts
|
|
3201
|
+
registerCommand({
|
|
3202
|
+
name: "encode",
|
|
3203
|
+
description: "Encode/decode text (base64, url, hex)",
|
|
3204
|
+
usage: "/encode <method> <encode|decode> <text>",
|
|
3205
|
+
async execute(args2) {
|
|
3206
|
+
if (args2.length < 2) {
|
|
3207
|
+
console.log("");
|
|
3208
|
+
console.log(theme.error("Usage: /encode <method> <encode|decode> <text>"));
|
|
3209
|
+
console.log(theme.dim(` Methods: ${SUPPORTED_METHODS.join(", ")}`));
|
|
3210
|
+
console.log(theme.dim(" Example: /encode base64 encode hello"));
|
|
3211
|
+
console.log(theme.dim(" Example: /encode url decode hello%20world"));
|
|
3212
|
+
console.log("");
|
|
3213
|
+
return;
|
|
3214
|
+
}
|
|
3215
|
+
const method = args2[0].toLowerCase();
|
|
3216
|
+
const action = args2[1].toLowerCase();
|
|
3217
|
+
const text = args2.slice(2).join(" ");
|
|
3218
|
+
if (!isValidMethod(method)) {
|
|
3219
|
+
console.log(theme.error(`Unknown method: ${method}. Use ${SUPPORTED_METHODS.join(", ")}`));
|
|
3220
|
+
return;
|
|
3221
|
+
}
|
|
3222
|
+
if (action !== "encode" && action !== "decode") {
|
|
3223
|
+
console.log(theme.error(`Unknown action: ${action}. Use encode or decode`));
|
|
3224
|
+
return;
|
|
3225
|
+
}
|
|
3226
|
+
if (!text) {
|
|
3227
|
+
console.log(theme.error("Please provide text to encode/decode"));
|
|
3228
|
+
return;
|
|
3229
|
+
}
|
|
3230
|
+
try {
|
|
3231
|
+
const result = encodeText(text, method, action);
|
|
3232
|
+
console.log("");
|
|
3233
|
+
console.log(box.draw([
|
|
3234
|
+
"",
|
|
3235
|
+
` ${symbols.sparkle} ${theme.gradient(result.method + " " + result.direction.toUpperCase())}`,
|
|
3236
|
+
"",
|
|
3237
|
+
` ${theme.dim("Input:")}`,
|
|
3238
|
+
` ${theme.secondary(result.input.length > 50 ? result.input.slice(0, 50) + "..." : result.input)}`,
|
|
3239
|
+
"",
|
|
3240
|
+
` ${theme.dim("Output:")}`,
|
|
3241
|
+
` ${theme.success(result.output.length > 50 ? result.output.slice(0, 50) + "..." : result.output)}`,
|
|
3242
|
+
"",
|
|
3243
|
+
...result.output.length > 50 ? [` ${theme.dim("(Full output: " + result.output.length + " chars)")}`] : [],
|
|
3244
|
+
""
|
|
3245
|
+
], 60));
|
|
3246
|
+
console.log("");
|
|
3247
|
+
if (result.output.length > 50) {
|
|
3248
|
+
console.log(theme.dim(" Full result:"));
|
|
3249
|
+
console.log(` ${result.output}`);
|
|
3250
|
+
console.log("");
|
|
3251
|
+
}
|
|
3252
|
+
} catch (e) {
|
|
3253
|
+
console.log(theme.error(`Failed to ${action}: Invalid input for ${method}`));
|
|
3254
|
+
}
|
|
3255
|
+
}
|
|
3256
|
+
});
|
|
3257
|
+
|
|
3258
|
+
// src/handlers/dev/json.ts
|
|
3259
|
+
function validateJson(input) {
|
|
3260
|
+
try {
|
|
3261
|
+
const data = JSON.parse(input);
|
|
3262
|
+
return { valid: true, data };
|
|
3263
|
+
} catch (error) {
|
|
3264
|
+
const message = error instanceof Error ? error.message : "Invalid JSON";
|
|
3265
|
+
return { valid: false, error: message };
|
|
3266
|
+
}
|
|
3267
|
+
}
|
|
3268
|
+
function formatJson(input, indent = 2) {
|
|
3269
|
+
try {
|
|
3270
|
+
const data = JSON.parse(input);
|
|
3271
|
+
const formatted = JSON.stringify(data, null, indent);
|
|
3272
|
+
return { valid: true, data, formatted };
|
|
3273
|
+
} catch (error) {
|
|
3274
|
+
const message = error instanceof Error ? error.message : "Invalid JSON";
|
|
3275
|
+
return { valid: false, error: message };
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3278
|
+
function minifyJson(input) {
|
|
3279
|
+
try {
|
|
3280
|
+
const data = JSON.parse(input);
|
|
3281
|
+
const formatted = JSON.stringify(data);
|
|
3282
|
+
return { valid: true, data, formatted };
|
|
3283
|
+
} catch (error) {
|
|
3284
|
+
const message = error instanceof Error ? error.message : "Invalid JSON";
|
|
3285
|
+
return { valid: false, error: message };
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
function queryJson(input, path) {
|
|
3289
|
+
try {
|
|
3290
|
+
const data = JSON.parse(input);
|
|
3291
|
+
const parts = path.replace(/^\$\.?/, "").split(".").filter(Boolean);
|
|
3292
|
+
let current = data;
|
|
3293
|
+
for (const part of parts) {
|
|
3294
|
+
const arrayMatch = part.match(/^(\w*)\[(\d+)\]$/);
|
|
3295
|
+
if (arrayMatch) {
|
|
3296
|
+
const [, key, index] = arrayMatch;
|
|
3297
|
+
if (key) {
|
|
3298
|
+
current = current[key];
|
|
3299
|
+
}
|
|
3300
|
+
if (Array.isArray(current)) {
|
|
3301
|
+
current = current[parseInt(index)];
|
|
3302
|
+
} else {
|
|
3303
|
+
return { valid: false, error: `Not an array at ${part}` };
|
|
3304
|
+
}
|
|
3305
|
+
} else {
|
|
3306
|
+
if (current && typeof current === "object") {
|
|
3307
|
+
current = current[part];
|
|
3308
|
+
} else {
|
|
3309
|
+
return { valid: false, error: `Cannot access ${part}` };
|
|
3310
|
+
}
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
return { valid: true, data: current, formatted: JSON.stringify(current, null, 2) };
|
|
3314
|
+
} catch (error) {
|
|
3315
|
+
const message = error instanceof Error ? error.message : "Invalid JSON";
|
|
3316
|
+
return { valid: false, error: message };
|
|
3317
|
+
}
|
|
3318
|
+
}
|
|
3319
|
+
function getJsonStats(input) {
|
|
3320
|
+
try {
|
|
3321
|
+
let countKeys2 = function(obj, depth = 0) {
|
|
3322
|
+
if (typeof obj !== "object" || obj === null) {
|
|
3323
|
+
return { keys: 0, maxDepth: depth };
|
|
3324
|
+
}
|
|
3325
|
+
let keys = 0;
|
|
3326
|
+
let maxDepth = depth;
|
|
3327
|
+
if (Array.isArray(obj)) {
|
|
3328
|
+
for (const item of obj) {
|
|
3329
|
+
const result = countKeys2(item, depth + 1);
|
|
3330
|
+
keys += result.keys;
|
|
3331
|
+
maxDepth = Math.max(maxDepth, result.maxDepth);
|
|
3332
|
+
}
|
|
3333
|
+
} else {
|
|
3334
|
+
keys = Object.keys(obj).length;
|
|
3335
|
+
for (const value of Object.values(obj)) {
|
|
3336
|
+
const result = countKeys2(value, depth + 1);
|
|
3337
|
+
keys += result.keys;
|
|
3338
|
+
maxDepth = Math.max(maxDepth, result.maxDepth);
|
|
3339
|
+
}
|
|
3340
|
+
}
|
|
3341
|
+
return { keys, maxDepth };
|
|
3342
|
+
};
|
|
3343
|
+
var countKeys = countKeys2;
|
|
3344
|
+
const data = JSON.parse(input);
|
|
3345
|
+
const stats = countKeys2(data);
|
|
3346
|
+
const size = new Blob([input]).size;
|
|
3347
|
+
const sizeStr = size < 1024 ? `${size}B` : size < 1024 * 1024 ? `${(size / 1024).toFixed(1)}KB` : `${(size / 1024 / 1024).toFixed(1)}MB`;
|
|
3348
|
+
return { keys: stats.keys, depth: stats.maxDepth, size: sizeStr };
|
|
3349
|
+
} catch {
|
|
3350
|
+
return null;
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
|
|
3354
|
+
// src/commands/dev/json.ts
|
|
3355
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
3356
|
+
registerCommand({
|
|
3357
|
+
name: "json",
|
|
3358
|
+
description: "JSON tools (validate, format, query)",
|
|
3359
|
+
usage: "/json <action> <input>\n\n Actions: validate, format, minify, query, stats",
|
|
3360
|
+
async execute(args2) {
|
|
3361
|
+
const action = args2[0]?.toLowerCase();
|
|
3362
|
+
const input = args2.slice(1).join(" ");
|
|
3363
|
+
if (!action) {
|
|
3364
|
+
console.log("");
|
|
3365
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("JSON TOOLS")}`);
|
|
3366
|
+
console.log("");
|
|
3367
|
+
console.log(` ${theme.dim("Usage:")} /json <action> <input>`);
|
|
3368
|
+
console.log("");
|
|
3369
|
+
console.log(` ${theme.dim("Actions:")}`);
|
|
3370
|
+
console.log(` ${theme.primary("validate")} <json|@file> ${theme.dim("Check if JSON is valid")}`);
|
|
3371
|
+
console.log(` ${theme.primary("format")} <json|@file> ${theme.dim("Pretty-print JSON")}`);
|
|
3372
|
+
console.log(` ${theme.primary("minify")} <json|@file> ${theme.dim("Minify JSON")}`);
|
|
3373
|
+
console.log(` ${theme.primary("query")} <path> <json> ${theme.dim("Query with path (e.g., users[0].name)")}`);
|
|
3374
|
+
console.log(` ${theme.primary("stats")} <json|@file> ${theme.dim("Show JSON statistics")}`);
|
|
3375
|
+
console.log("");
|
|
3376
|
+
console.log(` ${theme.dim("Use @filename to read from file")}`);
|
|
3377
|
+
console.log("");
|
|
3378
|
+
return;
|
|
3379
|
+
}
|
|
3380
|
+
let jsonContent = input;
|
|
3381
|
+
if (input.startsWith("@")) {
|
|
3382
|
+
const filePath = input.slice(1);
|
|
3383
|
+
if (!existsSync5(filePath)) {
|
|
3384
|
+
console.log("");
|
|
3385
|
+
console.log(` ${symbols.cross} ${theme.error(`File not found: ${filePath}`)}`);
|
|
3386
|
+
console.log("");
|
|
3387
|
+
return;
|
|
3388
|
+
}
|
|
3389
|
+
jsonContent = readFileSync3(filePath, "utf-8");
|
|
3390
|
+
}
|
|
3391
|
+
console.log("");
|
|
3392
|
+
switch (action) {
|
|
3393
|
+
case "validate": {
|
|
3394
|
+
if (!jsonContent) {
|
|
3395
|
+
console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json validate <json|@file>`);
|
|
3396
|
+
break;
|
|
3397
|
+
}
|
|
3398
|
+
const result = validateJson(jsonContent);
|
|
3399
|
+
if (result.valid) {
|
|
3400
|
+
console.log(` ${symbols.check} ${theme.success("Valid JSON")}`);
|
|
3401
|
+
} else {
|
|
3402
|
+
console.log(` ${symbols.cross} ${theme.error("Invalid JSON")}`);
|
|
3403
|
+
console.log(` ${theme.dim(result.error || "")}`);
|
|
3404
|
+
}
|
|
3405
|
+
break;
|
|
3406
|
+
}
|
|
3407
|
+
case "format":
|
|
3408
|
+
case "pretty": {
|
|
3409
|
+
if (!jsonContent) {
|
|
3410
|
+
console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json format <json|@file>`);
|
|
3411
|
+
break;
|
|
3412
|
+
}
|
|
3413
|
+
const result = formatJson(jsonContent);
|
|
3414
|
+
if (result.valid && result.formatted) {
|
|
3415
|
+
console.log(` ${symbols.check} ${theme.success("Formatted JSON:")}`);
|
|
3416
|
+
console.log("");
|
|
3417
|
+
for (const line of result.formatted.split("\n")) {
|
|
3418
|
+
console.log(` ${theme.primary(line)}`);
|
|
3419
|
+
}
|
|
3420
|
+
} else {
|
|
3421
|
+
console.log(` ${symbols.cross} ${theme.error("Invalid JSON")}`);
|
|
3422
|
+
console.log(` ${theme.dim(result.error || "")}`);
|
|
3423
|
+
}
|
|
3424
|
+
break;
|
|
3425
|
+
}
|
|
3426
|
+
case "minify":
|
|
3427
|
+
case "min": {
|
|
3428
|
+
if (!jsonContent) {
|
|
3429
|
+
console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json minify <json|@file>`);
|
|
3430
|
+
break;
|
|
3431
|
+
}
|
|
3432
|
+
const result = minifyJson(jsonContent);
|
|
3433
|
+
if (result.valid && result.formatted) {
|
|
3434
|
+
console.log(` ${symbols.check} ${theme.success("Minified:")}`);
|
|
3435
|
+
console.log("");
|
|
3436
|
+
console.log(` ${theme.primary(result.formatted)}`);
|
|
3437
|
+
} else {
|
|
3438
|
+
console.log(` ${symbols.cross} ${theme.error("Invalid JSON")}`);
|
|
3439
|
+
console.log(` ${theme.dim(result.error || "")}`);
|
|
3440
|
+
}
|
|
3441
|
+
break;
|
|
3442
|
+
}
|
|
3443
|
+
case "query":
|
|
3444
|
+
case "get": {
|
|
3445
|
+
const path = args2[1];
|
|
3446
|
+
const queryInput = args2.slice(2).join(" ");
|
|
3447
|
+
if (!path || !queryInput) {
|
|
3448
|
+
console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json query <path> <json|@file>`);
|
|
3449
|
+
console.log("");
|
|
3450
|
+
console.log(` ${theme.dim("Examples:")}`);
|
|
3451
|
+
console.log(` /json query name '{"name": "John"}'`);
|
|
3452
|
+
console.log(` /json query users[0].email @data.json`);
|
|
3453
|
+
break;
|
|
3454
|
+
}
|
|
3455
|
+
let queryJsonContent = queryInput;
|
|
3456
|
+
if (queryInput.startsWith("@")) {
|
|
3457
|
+
const filePath = queryInput.slice(1);
|
|
3458
|
+
if (!existsSync5(filePath)) {
|
|
3459
|
+
console.log(` ${symbols.cross} ${theme.error(`File not found: ${filePath}`)}`);
|
|
3460
|
+
break;
|
|
3461
|
+
}
|
|
3462
|
+
queryJsonContent = readFileSync3(filePath, "utf-8");
|
|
3463
|
+
}
|
|
3464
|
+
const result = queryJson(queryJsonContent, path);
|
|
3465
|
+
if (result.valid) {
|
|
3466
|
+
console.log(` ${symbols.check} ${theme.success(`Result for "${path}":`)} `);
|
|
3467
|
+
console.log("");
|
|
3468
|
+
if (result.formatted) {
|
|
3469
|
+
for (const line of result.formatted.split("\n")) {
|
|
3470
|
+
console.log(` ${theme.primary(line)}`);
|
|
3471
|
+
}
|
|
3472
|
+
} else {
|
|
3473
|
+
console.log(` ${theme.dim("undefined")}`);
|
|
3474
|
+
}
|
|
3475
|
+
} else {
|
|
3476
|
+
console.log(` ${symbols.cross} ${theme.error(result.error || "Query failed")}`);
|
|
3477
|
+
}
|
|
3478
|
+
break;
|
|
3479
|
+
}
|
|
3480
|
+
case "stats":
|
|
3481
|
+
case "info": {
|
|
3482
|
+
if (!jsonContent) {
|
|
3483
|
+
console.log(` ${symbols.warning} ${theme.warning("Usage:")} /json stats <json|@file>`);
|
|
3484
|
+
break;
|
|
3485
|
+
}
|
|
3486
|
+
const stats = getJsonStats(jsonContent);
|
|
3487
|
+
if (stats) {
|
|
3488
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("JSON STATS")}`);
|
|
3489
|
+
console.log("");
|
|
3490
|
+
console.log(` ${theme.dim("Total keys:")} ${theme.primary(stats.keys.toString())}`);
|
|
3491
|
+
console.log(` ${theme.dim("Max depth:")} ${theme.primary(stats.depth.toString())}`);
|
|
3492
|
+
console.log(` ${theme.dim("Size:")} ${theme.primary(stats.size)}`);
|
|
3493
|
+
} else {
|
|
3494
|
+
console.log(` ${symbols.cross} ${theme.error("Invalid JSON")}`);
|
|
3495
|
+
}
|
|
3496
|
+
break;
|
|
3497
|
+
}
|
|
3498
|
+
default:
|
|
3499
|
+
console.log(` ${symbols.cross} ${theme.error(`Unknown action: ${action}`)}`);
|
|
3500
|
+
console.log(` ${theme.dim("Run /json to see available actions")}`);
|
|
3501
|
+
}
|
|
3502
|
+
console.log("");
|
|
3503
|
+
}
|
|
3504
|
+
});
|
|
3505
|
+
|
|
3506
|
+
// src/handlers/dev/request.ts
|
|
3507
|
+
import https from "https";
|
|
3508
|
+
import http from "http";
|
|
3509
|
+
import { URL } from "url";
|
|
3510
|
+
async function makeRequest(urlStr, options = { method: "GET" }) {
|
|
3511
|
+
const startTime2 = Date.now();
|
|
3512
|
+
return new Promise((resolve6) => {
|
|
3513
|
+
const timeout = setTimeout(() => {
|
|
3514
|
+
resolve6({ success: false, error: "Request timed out" });
|
|
3515
|
+
}, options.timeout || 3e4);
|
|
3516
|
+
try {
|
|
3517
|
+
const url = new URL(urlStr.startsWith("http") ? urlStr : `https://${urlStr}`);
|
|
3518
|
+
const client = url.protocol === "https:" ? https : http;
|
|
3519
|
+
const reqOptions = {
|
|
3520
|
+
hostname: url.hostname,
|
|
3521
|
+
port: url.port || (url.protocol === "https:" ? 443 : 80),
|
|
3522
|
+
path: url.pathname + url.search,
|
|
3523
|
+
method: options.method.toUpperCase(),
|
|
3524
|
+
headers: {
|
|
3525
|
+
"User-Agent": "Zammy-CLI/1.0",
|
|
3526
|
+
...options.headers
|
|
3527
|
+
}
|
|
3528
|
+
};
|
|
3529
|
+
const req = client.request(reqOptions, (res) => {
|
|
3530
|
+
let body = "";
|
|
3531
|
+
res.on("data", (chunk) => {
|
|
3532
|
+
body += chunk.toString();
|
|
3533
|
+
});
|
|
3534
|
+
res.on("end", () => {
|
|
3535
|
+
clearTimeout(timeout);
|
|
3536
|
+
resolve6({
|
|
3537
|
+
success: true,
|
|
3538
|
+
statusCode: res.statusCode,
|
|
3539
|
+
statusMessage: res.statusMessage,
|
|
3540
|
+
headers: res.headers,
|
|
3541
|
+
body,
|
|
3542
|
+
time: Date.now() - startTime2
|
|
3543
|
+
});
|
|
3544
|
+
});
|
|
3545
|
+
});
|
|
3546
|
+
req.on("error", (error) => {
|
|
3547
|
+
clearTimeout(timeout);
|
|
3548
|
+
resolve6({ success: false, error: error.message });
|
|
3549
|
+
});
|
|
3550
|
+
if (options.body) {
|
|
3551
|
+
req.write(options.body);
|
|
3552
|
+
}
|
|
3553
|
+
req.end();
|
|
3554
|
+
} catch (error) {
|
|
3555
|
+
clearTimeout(timeout);
|
|
3556
|
+
resolve6({ success: false, error: error instanceof Error ? error.message : "Request failed" });
|
|
3557
|
+
}
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3560
|
+
function tryParseJson(body) {
|
|
3561
|
+
try {
|
|
3562
|
+
const parsed = JSON.parse(body);
|
|
3563
|
+
return { isJson: true, formatted: JSON.stringify(parsed, null, 2) };
|
|
3564
|
+
} catch {
|
|
3565
|
+
return { isJson: false };
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
|
|
3569
|
+
// src/commands/dev/request.ts
|
|
3570
|
+
registerCommand({
|
|
3571
|
+
name: "request",
|
|
3572
|
+
description: "Make HTTP requests",
|
|
3573
|
+
usage: "/request <method> <url> [options]",
|
|
3574
|
+
async execute(args2) {
|
|
3575
|
+
if (args2.length < 1) {
|
|
3576
|
+
console.log("");
|
|
3577
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("HTTP REQUEST")}`);
|
|
3578
|
+
console.log("");
|
|
3579
|
+
console.log(` ${theme.dim("Usage:")} /request <method> <url> [options]`);
|
|
3580
|
+
console.log("");
|
|
3581
|
+
console.log(` ${theme.dim("Methods:")} GET, POST, PUT, DELETE, PATCH, HEAD`);
|
|
3582
|
+
console.log("");
|
|
3583
|
+
console.log(` ${theme.dim("Examples:")}`);
|
|
3584
|
+
console.log(` /request GET https://api.github.com`);
|
|
3585
|
+
console.log(` /request POST https://httpbin.org/post --body '{"name":"test"}'`);
|
|
3586
|
+
console.log(` /request GET api.example.com/users`);
|
|
3587
|
+
console.log("");
|
|
3588
|
+
return;
|
|
3589
|
+
}
|
|
3590
|
+
let method = "GET";
|
|
3591
|
+
let url = args2[0];
|
|
3592
|
+
const methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS"];
|
|
3593
|
+
if (methods.includes(args2[0].toUpperCase())) {
|
|
3594
|
+
method = args2[0].toUpperCase();
|
|
3595
|
+
url = args2[1];
|
|
3596
|
+
}
|
|
3597
|
+
if (!url) {
|
|
3598
|
+
console.log("");
|
|
3599
|
+
console.log(` ${symbols.cross} ${theme.error("URL is required")}`);
|
|
3600
|
+
console.log("");
|
|
3601
|
+
return;
|
|
3602
|
+
}
|
|
3603
|
+
const headers = {};
|
|
3604
|
+
let body;
|
|
3605
|
+
for (let i = 2; i < args2.length; i++) {
|
|
3606
|
+
if (args2[i] === "--header" || args2[i] === "-H") {
|
|
3607
|
+
const header = args2[++i];
|
|
3608
|
+
if (header) {
|
|
3609
|
+
const [key, ...valueParts] = header.split(":");
|
|
3610
|
+
headers[key.trim()] = valueParts.join(":").trim();
|
|
3611
|
+
}
|
|
3612
|
+
} else if (args2[i] === "--body" || args2[i] === "-d") {
|
|
3613
|
+
body = args2[++i];
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
console.log("");
|
|
3617
|
+
console.log(` ${theme.dim(`${method} ${url}...`)}`);
|
|
3618
|
+
const result = await makeRequest(url, { method, headers, body });
|
|
3619
|
+
process.stdout.write("\x1B[1A\x1B[2K");
|
|
3620
|
+
if (!result.success) {
|
|
3621
|
+
console.log(` ${symbols.cross} ${theme.error(result.error || "Request failed")}`);
|
|
3622
|
+
console.log("");
|
|
3623
|
+
return;
|
|
3624
|
+
}
|
|
3625
|
+
const statusColor = result.statusCode && result.statusCode >= 200 && result.statusCode < 300 ? theme.success : result.statusCode && result.statusCode >= 400 ? theme.error : theme.warning;
|
|
3626
|
+
console.log(` ${statusColor(`${result.statusCode} ${result.statusMessage}`)} ${theme.dim(`(${result.time}ms)`)}`);
|
|
3627
|
+
console.log("");
|
|
3628
|
+
if (result.headers) {
|
|
3629
|
+
const importantHeaders = ["content-type", "content-length", "server", "date"];
|
|
3630
|
+
console.log(` ${theme.dim("Headers:")}`);
|
|
3631
|
+
for (const key of importantHeaders) {
|
|
3632
|
+
if (result.headers[key]) {
|
|
3633
|
+
console.log(` ${theme.secondary(key)}: ${result.headers[key]}`);
|
|
3634
|
+
}
|
|
3635
|
+
}
|
|
3636
|
+
console.log("");
|
|
3637
|
+
}
|
|
3638
|
+
if (result.body && method !== "HEAD") {
|
|
3639
|
+
console.log(` ${theme.dim("Body:")}`);
|
|
3640
|
+
const jsonResult = tryParseJson(result.body);
|
|
3641
|
+
if (jsonResult.isJson && jsonResult.formatted) {
|
|
3642
|
+
const lines = jsonResult.formatted.split("\n");
|
|
3643
|
+
const displayLines = lines.slice(0, 30);
|
|
3644
|
+
for (const line of displayLines) {
|
|
3645
|
+
console.log(` ${theme.primary(line)}`);
|
|
3646
|
+
}
|
|
3647
|
+
if (lines.length > 30) {
|
|
3648
|
+
console.log(` ${theme.dim(`... and ${lines.length - 30} more lines`)}`);
|
|
3649
|
+
}
|
|
3650
|
+
} else {
|
|
3651
|
+
const lines = result.body.split("\n").slice(0, 20);
|
|
3652
|
+
for (const line of lines) {
|
|
3653
|
+
console.log(` ${line.slice(0, 100)}`);
|
|
3654
|
+
}
|
|
3655
|
+
if (result.body.split("\n").length > 20) {
|
|
3656
|
+
console.log(` ${theme.dim("... (truncated)")}`);
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
}
|
|
3660
|
+
console.log("");
|
|
3661
|
+
}
|
|
3662
|
+
});
|
|
3663
|
+
|
|
3664
|
+
// src/handlers/dev/diff.ts
|
|
3665
|
+
import { readFileSync as readFileSync4, existsSync as existsSync6 } from "fs";
|
|
3666
|
+
function diffFiles(file1, file2) {
|
|
3667
|
+
if (!existsSync6(file1)) {
|
|
3668
|
+
return { success: false, lines: [], stats: { additions: 0, deletions: 0, unchanged: 0 }, error: `File not found: ${file1}` };
|
|
3669
|
+
}
|
|
3670
|
+
if (!existsSync6(file2)) {
|
|
3671
|
+
return { success: false, lines: [], stats: { additions: 0, deletions: 0, unchanged: 0 }, error: `File not found: ${file2}` };
|
|
3672
|
+
}
|
|
3673
|
+
try {
|
|
3674
|
+
const content1 = readFileSync4(file1, "utf-8");
|
|
3675
|
+
const content2 = readFileSync4(file2, "utf-8");
|
|
3676
|
+
return diffStrings(content1, content2);
|
|
3677
|
+
} catch (error) {
|
|
3678
|
+
return {
|
|
3679
|
+
success: false,
|
|
3680
|
+
lines: [],
|
|
3681
|
+
stats: { additions: 0, deletions: 0, unchanged: 0 },
|
|
3682
|
+
error: error instanceof Error ? error.message : "Failed to read files"
|
|
3683
|
+
};
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
function diffStrings(str1, str2) {
|
|
3687
|
+
const lines1 = str1.split("\n");
|
|
3688
|
+
const lines2 = str2.split("\n");
|
|
3689
|
+
const lcs = computeLCS(lines1, lines2);
|
|
3690
|
+
const diff = buildDiff(lines1, lines2, lcs);
|
|
3691
|
+
let additions = 0;
|
|
3692
|
+
let deletions = 0;
|
|
3693
|
+
let unchanged = 0;
|
|
3694
|
+
for (const line of diff) {
|
|
3695
|
+
if (line.type === "add") additions++;
|
|
3696
|
+
else if (line.type === "remove") deletions++;
|
|
3697
|
+
else if (line.type === "same") unchanged++;
|
|
3698
|
+
}
|
|
3699
|
+
return {
|
|
3700
|
+
success: true,
|
|
3701
|
+
lines: diff,
|
|
3702
|
+
stats: { additions, deletions, unchanged }
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
function computeLCS(lines1, lines2) {
|
|
3706
|
+
const m = lines1.length;
|
|
3707
|
+
const n = lines2.length;
|
|
3708
|
+
const dp = Array(m + 1).fill(null).map(() => Array(n + 1).fill(0));
|
|
3709
|
+
for (let i = 1; i <= m; i++) {
|
|
3710
|
+
for (let j = 1; j <= n; j++) {
|
|
3711
|
+
if (lines1[i - 1] === lines2[j - 1]) {
|
|
3712
|
+
dp[i][j] = dp[i - 1][j - 1] + 1;
|
|
3713
|
+
} else {
|
|
3714
|
+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
3715
|
+
}
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
return dp;
|
|
3719
|
+
}
|
|
3720
|
+
function buildDiff(lines1, lines2, lcs) {
|
|
3721
|
+
const result = [];
|
|
3722
|
+
let i = lines1.length;
|
|
3723
|
+
let j = lines2.length;
|
|
3724
|
+
const temp = [];
|
|
3725
|
+
while (i > 0 || j > 0) {
|
|
3726
|
+
if (i > 0 && j > 0 && lines1[i - 1] === lines2[j - 1]) {
|
|
3727
|
+
temp.push({ type: "same", content: lines1[i - 1], lineNum1: i, lineNum2: j });
|
|
3728
|
+
i--;
|
|
3729
|
+
j--;
|
|
3730
|
+
} else if (j > 0 && (i === 0 || lcs[i][j - 1] >= lcs[i - 1][j])) {
|
|
3731
|
+
temp.push({ type: "add", content: lines2[j - 1], lineNum2: j });
|
|
3732
|
+
j--;
|
|
3733
|
+
} else if (i > 0) {
|
|
3734
|
+
temp.push({ type: "remove", content: lines1[i - 1], lineNum1: i });
|
|
3735
|
+
i--;
|
|
3736
|
+
}
|
|
3737
|
+
}
|
|
3738
|
+
return temp.reverse();
|
|
3739
|
+
}
|
|
3740
|
+
function formatDiffStats(stats) {
|
|
3741
|
+
const parts = [];
|
|
3742
|
+
if (stats.additions > 0) parts.push(`+${stats.additions}`);
|
|
3743
|
+
if (stats.deletions > 0) parts.push(`-${stats.deletions}`);
|
|
3744
|
+
if (stats.unchanged > 0) parts.push(`=${stats.unchanged}`);
|
|
3745
|
+
return parts.join(", ");
|
|
3746
|
+
}
|
|
3747
|
+
|
|
3748
|
+
// src/commands/dev/diff.ts
|
|
3749
|
+
import { basename as basename2 } from "path";
|
|
3750
|
+
registerCommand({
|
|
3751
|
+
name: "diff",
|
|
3752
|
+
description: "Compare two files",
|
|
3753
|
+
usage: "/diff <file1> <file2>",
|
|
3754
|
+
async execute(args2) {
|
|
3755
|
+
if (args2.length < 2) {
|
|
3756
|
+
console.log("");
|
|
3757
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("FILE DIFF")}`);
|
|
3758
|
+
console.log("");
|
|
3759
|
+
console.log(` ${theme.dim("Usage:")} /diff <file1> <file2>`);
|
|
3760
|
+
console.log("");
|
|
3761
|
+
console.log(` ${theme.dim("Example:")}`);
|
|
3762
|
+
console.log(` /diff old.json new.json`);
|
|
3763
|
+
console.log(` /diff config.ts config.backup.ts`);
|
|
3764
|
+
console.log("");
|
|
3765
|
+
return;
|
|
3766
|
+
}
|
|
3767
|
+
const file1 = args2[0];
|
|
3768
|
+
const file2 = args2[1];
|
|
3769
|
+
console.log("");
|
|
3770
|
+
const result = diffFiles(file1, file2);
|
|
3771
|
+
if (!result.success) {
|
|
3772
|
+
console.log(` ${symbols.cross} ${theme.error(result.error || "Diff failed")}`);
|
|
3773
|
+
console.log("");
|
|
3774
|
+
return;
|
|
3775
|
+
}
|
|
3776
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("DIFF")}: ${theme.primary(basename2(file1))} ${theme.dim("\u2192")} ${theme.primary(basename2(file2))}`);
|
|
3777
|
+
console.log(` ${theme.dim(formatDiffStats(result.stats))}`);
|
|
3778
|
+
console.log("");
|
|
3779
|
+
if (result.stats.additions === 0 && result.stats.deletions === 0) {
|
|
3780
|
+
console.log(` ${symbols.check} ${theme.success("Files are identical")}`);
|
|
3781
|
+
console.log("");
|
|
3782
|
+
return;
|
|
3783
|
+
}
|
|
3784
|
+
let contextLines = 3;
|
|
3785
|
+
let lastShownIndex = -contextLines - 1;
|
|
3786
|
+
const linesToShow = [];
|
|
3787
|
+
result.lines.forEach((line, i) => {
|
|
3788
|
+
if (line.type !== "same") {
|
|
3789
|
+
for (let j = Math.max(0, i - contextLines); j <= Math.min(result.lines.length - 1, i + contextLines); j++) {
|
|
3790
|
+
if (!linesToShow.includes(j)) {
|
|
3791
|
+
linesToShow.push(j);
|
|
3792
|
+
}
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
});
|
|
3796
|
+
linesToShow.sort((a, b3) => a - b3);
|
|
3797
|
+
for (let i = 0; i < linesToShow.length; i++) {
|
|
3798
|
+
const lineIndex = linesToShow[i];
|
|
3799
|
+
const line = result.lines[lineIndex];
|
|
3800
|
+
if (i > 0 && linesToShow[i] - linesToShow[i - 1] > 1) {
|
|
3801
|
+
console.log(` ${theme.dim("...")}`);
|
|
3802
|
+
}
|
|
3803
|
+
const lineNum = line.lineNum1 || line.lineNum2 || "";
|
|
3804
|
+
const numStr = lineNum.toString().padStart(4);
|
|
3805
|
+
if (line.type === "add") {
|
|
3806
|
+
console.log(` ${theme.success("+")} ${theme.dim(numStr)} ${theme.success(line.content)}`);
|
|
3807
|
+
} else if (line.type === "remove") {
|
|
3808
|
+
console.log(` ${theme.error("-")} ${theme.dim(numStr)} ${theme.error(line.content)}`);
|
|
3809
|
+
} else {
|
|
3810
|
+
console.log(` ${theme.dim(" ")} ${theme.dim(numStr)} ${line.content}`);
|
|
3811
|
+
}
|
|
3812
|
+
}
|
|
3813
|
+
console.log("");
|
|
3814
|
+
}
|
|
3815
|
+
});
|
|
3816
|
+
|
|
3817
|
+
// src/commands/info/weather.ts
|
|
3818
|
+
registerCommand({
|
|
3819
|
+
name: "weather",
|
|
3820
|
+
description: "Get current weather for a city",
|
|
3821
|
+
usage: "/weather <city>",
|
|
3822
|
+
async execute(args2) {
|
|
3823
|
+
const city = args2.join(" ") || "London";
|
|
3824
|
+
console.log(theme.dim(`Fetching weather for ${city}...`));
|
|
3825
|
+
try {
|
|
3826
|
+
const response = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`);
|
|
3827
|
+
if (!response.ok) {
|
|
3828
|
+
console.log(theme.error(`Could not fetch weather for "${city}"`));
|
|
3829
|
+
return;
|
|
3830
|
+
}
|
|
3831
|
+
const data = await response.json();
|
|
3832
|
+
const current = data.current_condition[0];
|
|
3833
|
+
const location = data.nearest_area[0];
|
|
3834
|
+
const temp = current.temp_C;
|
|
3835
|
+
const feelsLike = current.FeelsLikeC;
|
|
3836
|
+
const desc = current.weatherDesc[0].value;
|
|
3837
|
+
const humidity = current.humidity;
|
|
3838
|
+
const wind = current.windspeedKmph;
|
|
3839
|
+
const cityName = location.areaName[0].value;
|
|
3840
|
+
const country = location.country[0].value;
|
|
3841
|
+
console.log("");
|
|
3842
|
+
console.log(theme.highlight(`${cityName}, ${country}`));
|
|
3843
|
+
console.log("");
|
|
3844
|
+
console.log(` ${theme.primary(desc)}`);
|
|
3845
|
+
console.log(` Temperature: ${theme.warning(temp + "\xB0C")} (feels like ${feelsLike}\xB0C)`);
|
|
3846
|
+
console.log(` Humidity: ${humidity}%`);
|
|
3847
|
+
console.log(` Wind: ${wind} km/h`);
|
|
3848
|
+
console.log("");
|
|
3849
|
+
} catch (error) {
|
|
3850
|
+
console.log(theme.error(`Error fetching weather: ${error}`));
|
|
3851
|
+
}
|
|
3852
|
+
}
|
|
3853
|
+
});
|
|
3854
|
+
|
|
3855
|
+
// src/plugins/loader.ts
|
|
3856
|
+
import { existsSync as existsSync8, readdirSync as readdirSync2, readFileSync as readFileSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
3857
|
+
import { join as join6, dirname as dirname3 } from "path";
|
|
3858
|
+
import { homedir as homedir3 } from "os";
|
|
3859
|
+
import { pathToFileURL, fileURLToPath as fileURLToPath2 } from "url";
|
|
3860
|
+
|
|
3861
|
+
// src/plugins/api.ts
|
|
3862
|
+
import { execSync, spawn as nodeSpawn } from "child_process";
|
|
3863
|
+
import { join as join5 } from "path";
|
|
3864
|
+
|
|
3865
|
+
// src/plugins/storage.ts
|
|
3866
|
+
import { existsSync as existsSync7, readFileSync as readFileSync5, writeFileSync as writeFileSync3, mkdirSync } from "fs";
|
|
3867
|
+
import { join as join4 } from "path";
|
|
3868
|
+
function createPluginStorage(pluginName, dataDir) {
|
|
3869
|
+
const storagePath = join4(dataDir, "data.json");
|
|
3870
|
+
if (!existsSync7(dataDir)) {
|
|
3871
|
+
mkdirSync(dataDir, { recursive: true });
|
|
3872
|
+
}
|
|
3873
|
+
function loadData() {
|
|
3874
|
+
try {
|
|
3875
|
+
if (existsSync7(storagePath)) {
|
|
3876
|
+
const content = readFileSync5(storagePath, "utf-8");
|
|
3877
|
+
return JSON.parse(content);
|
|
3878
|
+
}
|
|
3879
|
+
} catch {
|
|
3880
|
+
}
|
|
3881
|
+
return {};
|
|
3882
|
+
}
|
|
3883
|
+
function saveData(data) {
|
|
3884
|
+
try {
|
|
3885
|
+
writeFileSync3(storagePath, JSON.stringify(data, null, 2), "utf-8");
|
|
3886
|
+
} catch (error) {
|
|
3887
|
+
console.error(`Failed to save plugin storage for ${pluginName}:`, error);
|
|
3888
|
+
}
|
|
3889
|
+
}
|
|
3890
|
+
return {
|
|
3891
|
+
get(key) {
|
|
3892
|
+
const data = loadData();
|
|
3893
|
+
return data[key];
|
|
3894
|
+
},
|
|
3895
|
+
set(key, value) {
|
|
3896
|
+
const data = loadData();
|
|
3897
|
+
data[key] = value;
|
|
3898
|
+
saveData(data);
|
|
3899
|
+
},
|
|
3900
|
+
delete(key) {
|
|
3901
|
+
const data = loadData();
|
|
3902
|
+
delete data[key];
|
|
3903
|
+
saveData(data);
|
|
3904
|
+
},
|
|
3905
|
+
clear() {
|
|
3906
|
+
saveData({});
|
|
3907
|
+
},
|
|
3908
|
+
getAll() {
|
|
3909
|
+
return loadData();
|
|
3910
|
+
}
|
|
3911
|
+
};
|
|
3912
|
+
}
|
|
3913
|
+
|
|
3914
|
+
// src/plugins/api.ts
|
|
3915
|
+
import { readFileSync as readFileSync6 } from "fs";
|
|
3916
|
+
import { fileURLToPath } from "url";
|
|
3917
|
+
import { dirname as dirname2 } from "path";
|
|
3918
|
+
function getZammyVersion() {
|
|
3919
|
+
try {
|
|
3920
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
3921
|
+
const __dirname = dirname2(__filename);
|
|
3922
|
+
const pkgPath = join5(__dirname, "..", "..", "package.json");
|
|
3923
|
+
const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
|
|
3924
|
+
return pkg.version || "0.0.0";
|
|
3925
|
+
} catch {
|
|
3926
|
+
return "0.0.0";
|
|
3927
|
+
}
|
|
3928
|
+
}
|
|
3929
|
+
function createPluginUI() {
|
|
3930
|
+
return {
|
|
3931
|
+
theme: {
|
|
3932
|
+
primary: theme.primary,
|
|
3933
|
+
secondary: theme.secondary,
|
|
3934
|
+
accent: theme.accent,
|
|
3935
|
+
success: theme.success,
|
|
3936
|
+
warning: theme.warning,
|
|
3937
|
+
error: theme.error,
|
|
3938
|
+
info: theme.info,
|
|
3939
|
+
dim: theme.dim,
|
|
3940
|
+
gradient: theme.gradient
|
|
3941
|
+
},
|
|
3942
|
+
symbols: {
|
|
3943
|
+
check: symbols.check,
|
|
3944
|
+
cross: symbols.cross,
|
|
3945
|
+
star: symbols.star,
|
|
3946
|
+
arrow: symbols.arrow,
|
|
3947
|
+
bullet: symbols.bullet,
|
|
3948
|
+
folder: symbols.folder,
|
|
3949
|
+
file: "\u{1F4C4}",
|
|
3950
|
+
// 📄
|
|
3951
|
+
warning: symbols.warning,
|
|
3952
|
+
info: symbols.info,
|
|
3953
|
+
rocket: symbols.rocket,
|
|
3954
|
+
sparkles: symbols.sparkle
|
|
3955
|
+
},
|
|
3956
|
+
box: (content, options) => {
|
|
3957
|
+
const lines = content.split("\n");
|
|
3958
|
+
const width = Math.max(...lines.map((l) => l.replace(/\x1B\[[0-9;]*m/g, "").length)) + 4;
|
|
3959
|
+
return box.draw(lines, width);
|
|
3960
|
+
},
|
|
3961
|
+
progressBar: (current, total, width) => {
|
|
3962
|
+
const percent = Math.round(current / total * 100);
|
|
3963
|
+
return progressBar(percent, width || 30);
|
|
3964
|
+
}
|
|
3965
|
+
};
|
|
3966
|
+
}
|
|
3967
|
+
function createPluginLogger(pluginName) {
|
|
3968
|
+
const prefix = theme.dim(`[${pluginName}]`);
|
|
3969
|
+
return {
|
|
3970
|
+
info: (message) => console.log(`${prefix} ${theme.info(message)}`),
|
|
3971
|
+
warn: (message) => console.log(`${prefix} ${theme.warning(message)}`),
|
|
3972
|
+
error: (message) => console.log(`${prefix} ${theme.error(message)}`),
|
|
3973
|
+
debug: (message) => {
|
|
3974
|
+
if (process.env.ZAMMY_DEBUG) {
|
|
3975
|
+
console.log(`${prefix} ${theme.dim(message)}`);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
};
|
|
3979
|
+
}
|
|
3980
|
+
function createPluginShell(manifest) {
|
|
3981
|
+
if (!manifest.permissions?.shell) {
|
|
3982
|
+
return void 0;
|
|
3983
|
+
}
|
|
3984
|
+
return {
|
|
3985
|
+
exec: (command, options) => {
|
|
3986
|
+
try {
|
|
3987
|
+
return execSync(command, {
|
|
3988
|
+
encoding: "utf-8",
|
|
3989
|
+
timeout: options?.timeout || 3e4,
|
|
3990
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
3991
|
+
});
|
|
3992
|
+
} catch (error) {
|
|
3993
|
+
if (error && typeof error === "object" && "stdout" in error) {
|
|
3994
|
+
return error.stdout || "";
|
|
3995
|
+
}
|
|
3996
|
+
throw error;
|
|
3997
|
+
}
|
|
3998
|
+
},
|
|
3999
|
+
spawn: (command, args2) => {
|
|
4000
|
+
return new Promise((resolve6) => {
|
|
4001
|
+
const proc = nodeSpawn(command, args2 || [], {
|
|
4002
|
+
shell: true,
|
|
4003
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4004
|
+
});
|
|
4005
|
+
let stdout = "";
|
|
4006
|
+
let stderr = "";
|
|
4007
|
+
proc.stdout?.on("data", (data) => {
|
|
4008
|
+
stdout += data.toString();
|
|
4009
|
+
});
|
|
4010
|
+
proc.stderr?.on("data", (data) => {
|
|
4011
|
+
stderr += data.toString();
|
|
4012
|
+
});
|
|
4013
|
+
proc.on("close", (code) => {
|
|
4014
|
+
resolve6({ stdout, stderr, code: code || 0 });
|
|
4015
|
+
});
|
|
4016
|
+
proc.on("error", () => {
|
|
4017
|
+
resolve6({ stdout, stderr, code: 1 });
|
|
4018
|
+
});
|
|
4019
|
+
});
|
|
4020
|
+
}
|
|
4021
|
+
};
|
|
4022
|
+
}
|
|
4023
|
+
function createPluginAPI(manifest, pluginPath) {
|
|
4024
|
+
const dataDir = pluginPath;
|
|
4025
|
+
const context = {
|
|
4026
|
+
pluginName: manifest.name,
|
|
4027
|
+
pluginVersion: manifest.version,
|
|
4028
|
+
zammyVersion: getZammyVersion(),
|
|
4029
|
+
dataDir,
|
|
4030
|
+
cwd: process.cwd()
|
|
4031
|
+
};
|
|
4032
|
+
return {
|
|
4033
|
+
registerCommand: (command) => {
|
|
4034
|
+
registerPluginCommand(command, manifest.name);
|
|
4035
|
+
},
|
|
4036
|
+
registerCommands: (commands2) => {
|
|
4037
|
+
for (const command of commands2) {
|
|
4038
|
+
registerPluginCommand(command, manifest.name);
|
|
4039
|
+
}
|
|
4040
|
+
},
|
|
4041
|
+
ui: createPluginUI(),
|
|
4042
|
+
storage: createPluginStorage(manifest.name, dataDir),
|
|
4043
|
+
log: createPluginLogger(manifest.name),
|
|
4044
|
+
context,
|
|
4045
|
+
shell: createPluginShell(manifest)
|
|
4046
|
+
};
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4049
|
+
// src/plugins/loader.ts
|
|
4050
|
+
var PLUGINS_DIR = join6(homedir3(), ".zammy", "plugins");
|
|
4051
|
+
function getZammyVersion2() {
|
|
4052
|
+
try {
|
|
4053
|
+
const __filename = fileURLToPath2(import.meta.url);
|
|
4054
|
+
const __dirname = dirname3(__filename);
|
|
4055
|
+
const possiblePaths = [
|
|
4056
|
+
join6(__dirname, "package.json"),
|
|
4057
|
+
// Same dir (bundled: dist/)
|
|
4058
|
+
join6(__dirname, "..", "package.json"),
|
|
4059
|
+
// One up (bundled: project root)
|
|
4060
|
+
join6(__dirname, "..", "..", "package.json")
|
|
4061
|
+
// Two up (source: src/plugins/)
|
|
4062
|
+
];
|
|
4063
|
+
for (const pkgPath of possiblePaths) {
|
|
4064
|
+
if (existsSync8(pkgPath)) {
|
|
4065
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
|
|
4066
|
+
if (pkg.name === "zammy" && pkg.version) {
|
|
4067
|
+
return pkg.version;
|
|
4068
|
+
}
|
|
4069
|
+
}
|
|
4070
|
+
}
|
|
4071
|
+
return "0.0.0";
|
|
4072
|
+
} catch {
|
|
4073
|
+
return "0.0.0";
|
|
4074
|
+
}
|
|
4075
|
+
}
|
|
4076
|
+
function compareVersions(a, b3) {
|
|
4077
|
+
const partsA = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
4078
|
+
const partsB = b3.split(".").map((n) => parseInt(n, 10) || 0);
|
|
4079
|
+
for (let i = 0; i < 3; i++) {
|
|
4080
|
+
const numA = partsA[i] || 0;
|
|
4081
|
+
const numB = partsB[i] || 0;
|
|
4082
|
+
if (numA < numB) return -1;
|
|
4083
|
+
if (numA > numB) return 1;
|
|
4084
|
+
}
|
|
4085
|
+
return 0;
|
|
4086
|
+
}
|
|
4087
|
+
function checkVersionCompatibility(manifest, zammyVersion) {
|
|
4088
|
+
const minVersion = manifest.zammy?.minVersion;
|
|
4089
|
+
const maxVersion = manifest.zammy?.maxVersion;
|
|
4090
|
+
if (minVersion && compareVersions(zammyVersion, minVersion) < 0) {
|
|
4091
|
+
return {
|
|
4092
|
+
compatible: false,
|
|
4093
|
+
reason: `Requires Zammy v${minVersion}+, but you have v${zammyVersion}`
|
|
4094
|
+
};
|
|
4095
|
+
}
|
|
4096
|
+
if (maxVersion && compareVersions(zammyVersion, maxVersion) > 0) {
|
|
4097
|
+
return {
|
|
4098
|
+
compatible: false,
|
|
4099
|
+
reason: `Incompatible with Zammy v${zammyVersion} (max: v${maxVersion})`
|
|
4100
|
+
};
|
|
4101
|
+
}
|
|
4102
|
+
return { compatible: true };
|
|
4103
|
+
}
|
|
4104
|
+
var discoveredPlugins = /* @__PURE__ */ new Map();
|
|
4105
|
+
var loadedPlugins = /* @__PURE__ */ new Map();
|
|
4106
|
+
function ensurePluginsDir() {
|
|
4107
|
+
if (!existsSync8(PLUGINS_DIR)) {
|
|
4108
|
+
mkdirSync2(PLUGINS_DIR, { recursive: true });
|
|
4109
|
+
}
|
|
4110
|
+
}
|
|
4111
|
+
function getPluginsDir() {
|
|
4112
|
+
return PLUGINS_DIR;
|
|
4113
|
+
}
|
|
4114
|
+
async function discoverPlugins() {
|
|
4115
|
+
ensurePluginsDir();
|
|
4116
|
+
discoveredPlugins.clear();
|
|
4117
|
+
if (!existsSync8(PLUGINS_DIR)) {
|
|
4118
|
+
return [];
|
|
4119
|
+
}
|
|
4120
|
+
const zammyVersion = getZammyVersion2();
|
|
4121
|
+
const entries = readdirSync2(PLUGINS_DIR, { withFileTypes: true });
|
|
4122
|
+
const manifests = [];
|
|
4123
|
+
for (const entry of entries) {
|
|
4124
|
+
if (!entry.isDirectory()) continue;
|
|
4125
|
+
const pluginPath = join6(PLUGINS_DIR, entry.name);
|
|
4126
|
+
const manifestPath = join6(pluginPath, "zammy-plugin.json");
|
|
4127
|
+
if (!existsSync8(manifestPath)) continue;
|
|
4128
|
+
try {
|
|
4129
|
+
const manifestContent = readFileSync7(manifestPath, "utf-8");
|
|
4130
|
+
const manifest = JSON.parse(manifestContent);
|
|
4131
|
+
if (!manifest.name || !manifest.version || !manifest.main || !manifest.commands) {
|
|
4132
|
+
console.log(theme.warning(` ${symbols.warning} Invalid manifest for plugin in ${entry.name}`));
|
|
4133
|
+
continue;
|
|
4134
|
+
}
|
|
4135
|
+
const compatibility = checkVersionCompatibility(manifest, zammyVersion);
|
|
4136
|
+
if (!compatibility.compatible) {
|
|
4137
|
+
console.log(theme.warning(` ${symbols.warning} Plugin '${manifest.name}' skipped: ${compatibility.reason}`));
|
|
4138
|
+
continue;
|
|
4139
|
+
}
|
|
4140
|
+
discoveredPlugins.set(manifest.name, { manifest, path: pluginPath });
|
|
4141
|
+
manifests.push(manifest);
|
|
4142
|
+
} catch (error) {
|
|
4143
|
+
console.log(theme.warning(` ${symbols.warning} Failed to read manifest for ${entry.name}`));
|
|
4144
|
+
}
|
|
4145
|
+
}
|
|
4146
|
+
return manifests;
|
|
4147
|
+
}
|
|
4148
|
+
async function loadPlugin(name) {
|
|
4149
|
+
if (loadedPlugins.has(name)) {
|
|
4150
|
+
const existing = loadedPlugins.get(name);
|
|
4151
|
+
if (existing.state === "error") {
|
|
4152
|
+
return null;
|
|
4153
|
+
}
|
|
4154
|
+
return existing;
|
|
4155
|
+
}
|
|
4156
|
+
const discovered = discoveredPlugins.get(name);
|
|
4157
|
+
if (!discovered) {
|
|
4158
|
+
return null;
|
|
4159
|
+
}
|
|
4160
|
+
const { manifest, path: pluginPath } = discovered;
|
|
4161
|
+
try {
|
|
4162
|
+
const mainPath = join6(pluginPath, manifest.main);
|
|
4163
|
+
if (!existsSync8(mainPath)) {
|
|
4164
|
+
throw new Error(`Plugin entry point not found: ${mainPath}`);
|
|
4165
|
+
}
|
|
4166
|
+
const moduleUrl = pathToFileURL(mainPath).href;
|
|
4167
|
+
const module = await import(moduleUrl);
|
|
4168
|
+
const plugin = module.default;
|
|
4169
|
+
if (!plugin || typeof plugin.activate !== "function") {
|
|
4170
|
+
throw new Error("Plugin must export a default object with an activate function");
|
|
4171
|
+
}
|
|
4172
|
+
const api = createPluginAPI(manifest, pluginPath);
|
|
4173
|
+
try {
|
|
4174
|
+
await plugin.activate(api);
|
|
4175
|
+
} catch (activationError) {
|
|
4176
|
+
const msg = activationError instanceof Error ? activationError.message : String(activationError);
|
|
4177
|
+
throw new Error(`Plugin activation failed: ${msg}`);
|
|
4178
|
+
}
|
|
4179
|
+
const loaded = {
|
|
4180
|
+
manifest,
|
|
4181
|
+
instance: plugin,
|
|
4182
|
+
path: pluginPath,
|
|
4183
|
+
state: "active"
|
|
4184
|
+
};
|
|
4185
|
+
loadedPlugins.set(name, loaded);
|
|
4186
|
+
return loaded;
|
|
4187
|
+
} catch (error) {
|
|
4188
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4189
|
+
console.log(theme.error(` ${symbols.cross} Failed to load plugin '${name}': ${errorMessage}`));
|
|
4190
|
+
const failedPlugin = {
|
|
4191
|
+
manifest,
|
|
4192
|
+
instance: { activate: async () => {
|
|
4193
|
+
} },
|
|
4194
|
+
path: pluginPath,
|
|
4195
|
+
state: "error"
|
|
4196
|
+
};
|
|
4197
|
+
loadedPlugins.set(name, failedPlugin);
|
|
4198
|
+
return null;
|
|
4199
|
+
}
|
|
4200
|
+
}
|
|
4201
|
+
async function unloadPlugin(name) {
|
|
4202
|
+
const loaded = loadedPlugins.get(name);
|
|
4203
|
+
if (!loaded) {
|
|
4204
|
+
return false;
|
|
4205
|
+
}
|
|
4206
|
+
try {
|
|
4207
|
+
if (loaded.instance.deactivate) {
|
|
4208
|
+
await loaded.instance.deactivate();
|
|
4209
|
+
}
|
|
4210
|
+
unregisterPluginCommands(name);
|
|
4211
|
+
loadedPlugins.delete(name);
|
|
4212
|
+
return true;
|
|
4213
|
+
} catch (error) {
|
|
4214
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4215
|
+
console.log(theme.error(` ${symbols.cross} Failed to unload plugin '${name}': ${errorMessage}`));
|
|
4216
|
+
return false;
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
function getDiscoveredPlugins() {
|
|
4220
|
+
return Array.from(discoveredPlugins.values()).map((p) => p.manifest);
|
|
4221
|
+
}
|
|
4222
|
+
function isPluginLoaded(name) {
|
|
4223
|
+
return loadedPlugins.has(name);
|
|
4224
|
+
}
|
|
4225
|
+
async function initPluginLoader() {
|
|
4226
|
+
const manifests = await discoverPlugins();
|
|
4227
|
+
for (const manifest of manifests) {
|
|
4228
|
+
for (const cmdName of manifest.commands) {
|
|
4229
|
+
const lazyExecute = async (args2) => {
|
|
4230
|
+
const loaded = await loadPlugin(manifest.name);
|
|
4231
|
+
if (!loaded) {
|
|
4232
|
+
console.log(theme.error(` ${symbols.cross} Failed to load plugin '${manifest.name}'`));
|
|
4233
|
+
console.log(theme.dim(` Try reinstalling: /plugin remove ${manifest.name} && /plugin install ${manifest.name}`));
|
|
4234
|
+
return;
|
|
4235
|
+
}
|
|
4236
|
+
const realCommand = getCommand(cmdName);
|
|
4237
|
+
if (realCommand && realCommand.execute !== lazyExecute) {
|
|
4238
|
+
await realCommand.execute(args2);
|
|
4239
|
+
} else {
|
|
4240
|
+
console.log(theme.error(` ${symbols.cross} Plugin '${manifest.name}' did not register command '${cmdName}'`));
|
|
4241
|
+
console.log(theme.dim(` The plugin may be misconfigured or corrupted.`));
|
|
4242
|
+
}
|
|
4243
|
+
};
|
|
4244
|
+
registerPluginCommand(
|
|
4245
|
+
{
|
|
4246
|
+
name: cmdName,
|
|
4247
|
+
description: `[${manifest.displayName || manifest.name}] Plugin command`,
|
|
4248
|
+
usage: `/${cmdName}`,
|
|
4249
|
+
execute: lazyExecute
|
|
4250
|
+
},
|
|
4251
|
+
manifest.name
|
|
4252
|
+
);
|
|
4253
|
+
}
|
|
4254
|
+
}
|
|
4255
|
+
}
|
|
4256
|
+
|
|
4257
|
+
// src/commands/plugin/list.ts
|
|
4258
|
+
async function listPlugins() {
|
|
4259
|
+
await discoverPlugins();
|
|
4260
|
+
const plugins = getDiscoveredPlugins();
|
|
4261
|
+
console.log("");
|
|
4262
|
+
if (plugins.length === 0) {
|
|
4263
|
+
console.log(` ${symbols.info} ${theme.dim("No plugins installed")}`);
|
|
4264
|
+
console.log("");
|
|
4265
|
+
console.log(` ${theme.dim("Install a plugin with:")} ${theme.primary("/plugin install <source>")}`);
|
|
4266
|
+
console.log(` ${theme.dim("Create a new plugin with:")} ${theme.primary("/plugin create")}`);
|
|
4267
|
+
console.log("");
|
|
4268
|
+
return;
|
|
4269
|
+
}
|
|
4270
|
+
console.log(` ${symbols.folder} ${theme.gradient("INSTALLED PLUGINS")} ${theme.dim(`(${plugins.length})`)}`);
|
|
4271
|
+
console.log("");
|
|
4272
|
+
for (const plugin of plugins) {
|
|
4273
|
+
const loaded = isPluginLoaded(plugin.name);
|
|
4274
|
+
const status = loaded ? theme.success("active") : theme.dim("idle");
|
|
4275
|
+
const statusIcon = loaded ? symbols.check : symbols.bullet;
|
|
4276
|
+
console.log(` ${statusIcon} ${theme.primary(plugin.displayName || plugin.name)} ${theme.dim(`v${plugin.version}`)}`);
|
|
4277
|
+
if (plugin.description) {
|
|
4278
|
+
console.log(` ${theme.dim(plugin.description)}`);
|
|
4279
|
+
}
|
|
4280
|
+
console.log(` ${theme.dim("Commands:")} ${plugin.commands.map((c) => theme.accent("/" + c)).join(", ")}`);
|
|
4281
|
+
if (plugin.permissions) {
|
|
4282
|
+
const perms = [];
|
|
4283
|
+
if (plugin.permissions.shell) perms.push("shell");
|
|
4284
|
+
if (plugin.permissions.filesystem) perms.push("fs");
|
|
4285
|
+
if (plugin.permissions.network) perms.push("net");
|
|
4286
|
+
if (perms.length > 0) {
|
|
4287
|
+
console.log(` ${theme.dim("Permissions:")} ${theme.warning(perms.join(", "))}`);
|
|
4288
|
+
}
|
|
4289
|
+
}
|
|
4290
|
+
console.log("");
|
|
4291
|
+
}
|
|
4292
|
+
}
|
|
4293
|
+
|
|
4294
|
+
// src/plugins/installer.ts
|
|
4295
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, cpSync, rmSync, readFileSync as readFileSync8, readdirSync as readdirSync3, createReadStream, createWriteStream } from "fs";
|
|
4296
|
+
import { join as join7, resolve as resolve3, dirname as dirname4 } from "path";
|
|
4297
|
+
import { execSync as execSync2 } from "child_process";
|
|
4298
|
+
import { tmpdir, platform as platform2 } from "os";
|
|
4299
|
+
import { createGunzip } from "zlib";
|
|
4300
|
+
import { pipeline } from "stream/promises";
|
|
4301
|
+
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
4302
|
+
var isWindows = platform2() === "win32";
|
|
4303
|
+
function getZammyVersion3() {
|
|
4304
|
+
try {
|
|
4305
|
+
const __filename = fileURLToPath3(import.meta.url);
|
|
4306
|
+
const __dirname = dirname4(__filename);
|
|
4307
|
+
const possiblePaths = [
|
|
4308
|
+
join7(__dirname, "package.json"),
|
|
4309
|
+
join7(__dirname, "..", "package.json"),
|
|
4310
|
+
join7(__dirname, "..", "..", "package.json")
|
|
4311
|
+
];
|
|
4312
|
+
for (const pkgPath of possiblePaths) {
|
|
4313
|
+
if (existsSync9(pkgPath)) {
|
|
4314
|
+
const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
|
|
4315
|
+
if (pkg.name === "zammy" && pkg.version) {
|
|
4316
|
+
return pkg.version;
|
|
4317
|
+
}
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
return "0.0.0";
|
|
4321
|
+
} catch {
|
|
4322
|
+
return "0.0.0";
|
|
4323
|
+
}
|
|
4324
|
+
}
|
|
4325
|
+
function compareVersions2(a, b3) {
|
|
4326
|
+
const partsA = a.split(".").map((n) => parseInt(n, 10) || 0);
|
|
4327
|
+
const partsB = b3.split(".").map((n) => parseInt(n, 10) || 0);
|
|
4328
|
+
for (let i = 0; i < 3; i++) {
|
|
4329
|
+
const numA = partsA[i] || 0;
|
|
4330
|
+
const numB = partsB[i] || 0;
|
|
4331
|
+
if (numA < numB) return -1;
|
|
4332
|
+
if (numA > numB) return 1;
|
|
4333
|
+
}
|
|
4334
|
+
return 0;
|
|
4335
|
+
}
|
|
4336
|
+
function checkVersionCompatibility2(manifest) {
|
|
4337
|
+
const zammyVersion = getZammyVersion3();
|
|
4338
|
+
const minVersion = manifest.zammy?.minVersion;
|
|
4339
|
+
const maxVersion = manifest.zammy?.maxVersion;
|
|
4340
|
+
if (minVersion && compareVersions2(zammyVersion, minVersion) < 0) {
|
|
4341
|
+
return {
|
|
4342
|
+
compatible: false,
|
|
4343
|
+
reason: `Requires Zammy v${minVersion}+, but you have v${zammyVersion}`
|
|
4344
|
+
};
|
|
4345
|
+
}
|
|
4346
|
+
if (maxVersion && compareVersions2(zammyVersion, maxVersion) > 0) {
|
|
4347
|
+
return {
|
|
4348
|
+
compatible: false,
|
|
4349
|
+
reason: `Incompatible with Zammy v${zammyVersion} (max supported: v${maxVersion})`
|
|
4350
|
+
};
|
|
4351
|
+
}
|
|
4352
|
+
return { compatible: true };
|
|
4353
|
+
}
|
|
4354
|
+
async function extractTarGz(tarGzPath, destDir) {
|
|
4355
|
+
try {
|
|
4356
|
+
execSync2(`tar -xzf "${tarGzPath}" -C "${destDir}"`, {
|
|
4357
|
+
encoding: "utf-8",
|
|
4358
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4359
|
+
});
|
|
4360
|
+
return;
|
|
4361
|
+
} catch {
|
|
4362
|
+
}
|
|
4363
|
+
const tarPath = tarGzPath.replace(/\.tgz$|\.tar\.gz$/, ".tar");
|
|
4364
|
+
await pipeline(
|
|
4365
|
+
createReadStream(tarGzPath),
|
|
4366
|
+
createGunzip(),
|
|
4367
|
+
createWriteStream(tarPath)
|
|
4368
|
+
);
|
|
4369
|
+
try {
|
|
4370
|
+
execSync2(`tar -xf "${tarPath}" -C "${destDir}"`, {
|
|
4371
|
+
encoding: "utf-8",
|
|
4372
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4373
|
+
});
|
|
4374
|
+
rmSync(tarPath, { force: true });
|
|
4375
|
+
return;
|
|
4376
|
+
} catch {
|
|
4377
|
+
rmSync(tarPath, { force: true });
|
|
4378
|
+
}
|
|
4379
|
+
if (isWindows) {
|
|
4380
|
+
try {
|
|
4381
|
+
execSync2(`powershell -Command "tar -xf '${tarPath}' -C '${destDir}'"`, {
|
|
4382
|
+
encoding: "utf-8",
|
|
4383
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
4384
|
+
});
|
|
4385
|
+
return;
|
|
4386
|
+
} catch {
|
|
4387
|
+
throw new Error(
|
|
4388
|
+
"Unable to extract plugin archive. Please ensure tar is available.\nOn Windows 10+, tar should be built-in. Try running: tar --version"
|
|
4389
|
+
);
|
|
4390
|
+
}
|
|
4391
|
+
}
|
|
4392
|
+
throw new Error("Unable to extract plugin archive. Please ensure tar is installed.");
|
|
4393
|
+
}
|
|
4394
|
+
function validateManifest(manifest) {
|
|
4395
|
+
const errors = [];
|
|
4396
|
+
if (!manifest || typeof manifest !== "object") {
|
|
4397
|
+
return { valid: false, errors: ["Manifest must be an object"] };
|
|
4398
|
+
}
|
|
4399
|
+
const m = manifest;
|
|
4400
|
+
if (!m.name || typeof m.name !== "string") {
|
|
4401
|
+
errors.push('Missing or invalid "name" field');
|
|
4402
|
+
}
|
|
4403
|
+
if (!m.version || typeof m.version !== "string") {
|
|
4404
|
+
errors.push('Missing or invalid "version" field');
|
|
4405
|
+
}
|
|
4406
|
+
if (!m.main || typeof m.main !== "string") {
|
|
4407
|
+
errors.push('Missing or invalid "main" field');
|
|
4408
|
+
}
|
|
4409
|
+
if (!m.commands || !Array.isArray(m.commands) || m.commands.length === 0) {
|
|
4410
|
+
errors.push('Missing or invalid "commands" field (must be non-empty array)');
|
|
4411
|
+
}
|
|
4412
|
+
if (!m.zammy || typeof m.zammy !== "object") {
|
|
4413
|
+
errors.push('Missing or invalid "zammy" field');
|
|
4414
|
+
} else {
|
|
4415
|
+
const zammy = m.zammy;
|
|
4416
|
+
if (!zammy.minVersion || typeof zammy.minVersion !== "string") {
|
|
4417
|
+
errors.push('Missing or invalid "zammy.minVersion" field');
|
|
4418
|
+
}
|
|
4419
|
+
}
|
|
4420
|
+
return { valid: errors.length === 0, errors };
|
|
4421
|
+
}
|
|
4422
|
+
function checkConflicts(manifest) {
|
|
4423
|
+
const conflicts = [];
|
|
4424
|
+
for (const cmd of manifest.commands) {
|
|
4425
|
+
const conflict = checkCommandConflict(cmd);
|
|
4426
|
+
if (conflict.exists) {
|
|
4427
|
+
if (conflict.source === "core") {
|
|
4428
|
+
conflicts.push(`Command '/${cmd}' conflicts with core zammy command`);
|
|
4429
|
+
} else {
|
|
4430
|
+
conflicts.push(`Command '/${cmd}' conflicts with plugin '${conflict.pluginName}'`);
|
|
4431
|
+
}
|
|
4432
|
+
}
|
|
4433
|
+
}
|
|
4434
|
+
return { hasConflicts: conflicts.length > 0, conflicts };
|
|
4435
|
+
}
|
|
4436
|
+
function formatPermissions(manifest) {
|
|
4437
|
+
const perms = [];
|
|
4438
|
+
const p = manifest.permissions;
|
|
4439
|
+
if (!p) return perms;
|
|
4440
|
+
if (p.shell) {
|
|
4441
|
+
perms.push(`${symbols.warning} shell: Can run system commands`);
|
|
4442
|
+
}
|
|
4443
|
+
if (p.filesystem) {
|
|
4444
|
+
if (p.filesystem === true) {
|
|
4445
|
+
perms.push(`${symbols.warning} filesystem: Full file system access`);
|
|
4446
|
+
} else if (Array.isArray(p.filesystem)) {
|
|
4447
|
+
perms.push(`${symbols.info} filesystem: Access to ${p.filesystem.join(", ")}`);
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
if (p.network) {
|
|
4451
|
+
if (p.network === true) {
|
|
4452
|
+
perms.push(`${symbols.warning} network: Full network access`);
|
|
4453
|
+
} else if (Array.isArray(p.network)) {
|
|
4454
|
+
perms.push(`${symbols.info} network: Access to ${p.network.join(", ")}`);
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
return perms;
|
|
4458
|
+
}
|
|
4459
|
+
async function installFromLocal(sourcePath) {
|
|
4460
|
+
try {
|
|
4461
|
+
const absPath = resolve3(sourcePath);
|
|
4462
|
+
if (!existsSync9(absPath)) {
|
|
4463
|
+
return { success: false, error: `Path not found: ${absPath}` };
|
|
4464
|
+
}
|
|
4465
|
+
const manifestPath = join7(absPath, "zammy-plugin.json");
|
|
4466
|
+
if (!existsSync9(manifestPath)) {
|
|
4467
|
+
return { success: false, error: "No zammy-plugin.json found in source directory" };
|
|
4468
|
+
}
|
|
4469
|
+
const manifestContent = readFileSync8(manifestPath, "utf-8");
|
|
4470
|
+
let manifest;
|
|
4471
|
+
try {
|
|
4472
|
+
manifest = JSON.parse(manifestContent);
|
|
4473
|
+
} catch {
|
|
4474
|
+
return { success: false, error: "Invalid JSON in zammy-plugin.json" };
|
|
4475
|
+
}
|
|
4476
|
+
const validation = validateManifest(manifest);
|
|
4477
|
+
if (!validation.valid) {
|
|
4478
|
+
return { success: false, error: `Invalid manifest: ${validation.errors.join(", ")}` };
|
|
4479
|
+
}
|
|
4480
|
+
const versionCheck = checkVersionCompatibility2(manifest);
|
|
4481
|
+
if (!versionCheck.compatible) {
|
|
4482
|
+
return { success: false, error: versionCheck.reason };
|
|
4483
|
+
}
|
|
4484
|
+
const mainPath = join7(absPath, manifest.main);
|
|
4485
|
+
if (!existsSync9(mainPath)) {
|
|
4486
|
+
return { success: false, error: `Entry point not found: ${manifest.main}` };
|
|
4487
|
+
}
|
|
4488
|
+
ensurePluginsDir();
|
|
4489
|
+
const targetDir = join7(getPluginsDir(), manifest.name);
|
|
4490
|
+
if (existsSync9(targetDir)) {
|
|
4491
|
+
rmSync(targetDir, { recursive: true, force: true });
|
|
4492
|
+
}
|
|
4493
|
+
cpSync(absPath, targetDir, { recursive: true });
|
|
4494
|
+
return { success: true, manifest };
|
|
4495
|
+
} catch (error) {
|
|
4496
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4497
|
+
return { success: false, error: message };
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
async function installFromNpm(packageName) {
|
|
4501
|
+
const tempDir = join7(tmpdir(), `zammy-plugin-${Date.now()}`);
|
|
4502
|
+
try {
|
|
4503
|
+
mkdirSync3(tempDir, { recursive: true });
|
|
4504
|
+
console.log(theme.dim(` Downloading ${packageName}...`));
|
|
4505
|
+
execSync2(`npm pack ${packageName} --pack-destination="${tempDir}"`, {
|
|
4506
|
+
encoding: "utf-8",
|
|
4507
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4508
|
+
timeout: 6e4
|
|
4509
|
+
});
|
|
4510
|
+
const files = readdirSync3(tempDir);
|
|
4511
|
+
const tarball = files.find((f) => f.endsWith(".tgz"));
|
|
4512
|
+
if (!tarball) {
|
|
4513
|
+
return { success: false, error: "Failed to download package" };
|
|
4514
|
+
}
|
|
4515
|
+
const extractDir = join7(tempDir, "extract");
|
|
4516
|
+
mkdirSync3(extractDir);
|
|
4517
|
+
await extractTarGz(join7(tempDir, tarball), extractDir);
|
|
4518
|
+
const packageDir = join7(extractDir, "package");
|
|
4519
|
+
if (!existsSync9(packageDir)) {
|
|
4520
|
+
return { success: false, error: "Invalid package structure" };
|
|
4521
|
+
}
|
|
4522
|
+
return await installFromLocal(packageDir);
|
|
4523
|
+
} catch (error) {
|
|
4524
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4525
|
+
return { success: false, error: `npm install failed: ${message}` };
|
|
4526
|
+
} finally {
|
|
4527
|
+
try {
|
|
4528
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4529
|
+
} catch {
|
|
4530
|
+
}
|
|
4531
|
+
}
|
|
4532
|
+
}
|
|
4533
|
+
async function installFromGithub(repo) {
|
|
4534
|
+
const tempDir = join7(tmpdir(), `zammy-plugin-${Date.now()}`);
|
|
4535
|
+
try {
|
|
4536
|
+
let repoPath = repo.replace(/^github:/, "");
|
|
4537
|
+
let branch = "main";
|
|
4538
|
+
if (repoPath.includes("#")) {
|
|
4539
|
+
[repoPath, branch] = repoPath.split("#");
|
|
4540
|
+
}
|
|
4541
|
+
mkdirSync3(tempDir, { recursive: true });
|
|
4542
|
+
console.log(theme.dim(` Cloning ${repoPath}...`));
|
|
4543
|
+
execSync2(`git clone --depth 1 --branch ${branch} https://github.com/${repoPath}.git "${tempDir}"`, {
|
|
4544
|
+
encoding: "utf-8",
|
|
4545
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4546
|
+
timeout: 12e4
|
|
4547
|
+
});
|
|
4548
|
+
const pkgJsonPath = join7(tempDir, "package.json");
|
|
4549
|
+
if (existsSync9(pkgJsonPath)) {
|
|
4550
|
+
const pkgJson = JSON.parse(readFileSync8(pkgJsonPath, "utf-8"));
|
|
4551
|
+
if (pkgJson.scripts?.build) {
|
|
4552
|
+
console.log(theme.dim(` Installing dependencies...`));
|
|
4553
|
+
execSync2("npm install", { cwd: tempDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
|
|
4554
|
+
console.log(theme.dim(` Building...`));
|
|
4555
|
+
execSync2("npm run build", { cwd: tempDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
|
|
4556
|
+
}
|
|
4557
|
+
}
|
|
4558
|
+
return await installFromLocal(tempDir);
|
|
4559
|
+
} catch (error) {
|
|
4560
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4561
|
+
return { success: false, error: `GitHub install failed: ${message}` };
|
|
4562
|
+
} finally {
|
|
4563
|
+
try {
|
|
4564
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4565
|
+
} catch {
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
}
|
|
4569
|
+
async function installFromGit(url) {
|
|
4570
|
+
const tempDir = join7(tmpdir(), `zammy-plugin-${Date.now()}`);
|
|
4571
|
+
try {
|
|
4572
|
+
mkdirSync3(tempDir, { recursive: true });
|
|
4573
|
+
console.log(theme.dim(` Cloning from ${url}...`));
|
|
4574
|
+
execSync2(`git clone --depth 1 "${url}" "${tempDir}"`, {
|
|
4575
|
+
encoding: "utf-8",
|
|
4576
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
4577
|
+
timeout: 12e4
|
|
4578
|
+
});
|
|
4579
|
+
const pkgJsonPath = join7(tempDir, "package.json");
|
|
4580
|
+
if (existsSync9(pkgJsonPath)) {
|
|
4581
|
+
const pkgJson = JSON.parse(readFileSync8(pkgJsonPath, "utf-8"));
|
|
4582
|
+
if (pkgJson.scripts?.build) {
|
|
4583
|
+
console.log(theme.dim(` Installing dependencies...`));
|
|
4584
|
+
execSync2("npm install", { cwd: tempDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
|
|
4585
|
+
console.log(theme.dim(` Building...`));
|
|
4586
|
+
execSync2("npm run build", { cwd: tempDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"], timeout: 12e4 });
|
|
4587
|
+
}
|
|
4588
|
+
}
|
|
4589
|
+
return await installFromLocal(tempDir);
|
|
4590
|
+
} catch (error) {
|
|
4591
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4592
|
+
return { success: false, error: `Git install failed: ${message}` };
|
|
4593
|
+
} finally {
|
|
4594
|
+
try {
|
|
4595
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
4596
|
+
} catch {
|
|
4597
|
+
}
|
|
4598
|
+
}
|
|
4599
|
+
}
|
|
4600
|
+
function removePlugin(name) {
|
|
4601
|
+
try {
|
|
4602
|
+
const pluginDir = join7(getPluginsDir(), name);
|
|
4603
|
+
if (!existsSync9(pluginDir)) {
|
|
4604
|
+
return { success: false, error: `Plugin '${name}' not found` };
|
|
4605
|
+
}
|
|
4606
|
+
rmSync(pluginDir, { recursive: true, force: true });
|
|
4607
|
+
return { success: true };
|
|
4608
|
+
} catch (error) {
|
|
4609
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4610
|
+
return { success: false, error: message };
|
|
4611
|
+
}
|
|
4612
|
+
}
|
|
4613
|
+
function detectSourceType(source) {
|
|
4614
|
+
if (source.startsWith("./") || source.startsWith("/") || source.startsWith("..") || /^[A-Za-z]:/.test(source)) {
|
|
4615
|
+
return "local";
|
|
4616
|
+
}
|
|
4617
|
+
if (source.startsWith("github:")) {
|
|
4618
|
+
return "github";
|
|
4619
|
+
}
|
|
4620
|
+
if (source.includes("github.com")) {
|
|
4621
|
+
return "github";
|
|
4622
|
+
}
|
|
4623
|
+
if (source.endsWith(".git") || source.startsWith("git@") || source.startsWith("git://")) {
|
|
4624
|
+
return "git";
|
|
4625
|
+
}
|
|
4626
|
+
if (/^(@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(source)) {
|
|
4627
|
+
return "npm";
|
|
4628
|
+
}
|
|
4629
|
+
return "unknown";
|
|
4630
|
+
}
|
|
4631
|
+
|
|
4632
|
+
// src/commands/plugin/install.ts
|
|
4633
|
+
import * as readline from "readline";
|
|
4634
|
+
async function confirm(message) {
|
|
4635
|
+
const rl = readline.createInterface({
|
|
4636
|
+
input: process.stdin,
|
|
4637
|
+
output: process.stdout
|
|
4638
|
+
});
|
|
4639
|
+
return new Promise((resolve6) => {
|
|
4640
|
+
rl.question(`${message} [y/N] `, (answer) => {
|
|
4641
|
+
rl.close();
|
|
4642
|
+
resolve6(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
4643
|
+
});
|
|
4644
|
+
});
|
|
4645
|
+
}
|
|
4646
|
+
async function installPlugin(args2) {
|
|
4647
|
+
const source = args2[0];
|
|
4648
|
+
if (!source) {
|
|
4649
|
+
console.log(theme.error(` ${symbols.cross} No source specified`));
|
|
4650
|
+
console.log("");
|
|
4651
|
+
console.log(` ${theme.primary("Usage:")} /plugin install <source>`);
|
|
4652
|
+
console.log("");
|
|
4653
|
+
console.log(` ${theme.dim("Examples:")}`);
|
|
4654
|
+
console.log(` ${theme.dim("/plugin install ./my-plugin")}`);
|
|
4655
|
+
console.log(` ${theme.dim("/plugin install zammy-plugin-git")}`);
|
|
4656
|
+
console.log(` ${theme.dim("/plugin install github:user/repo")}`);
|
|
4657
|
+
console.log("");
|
|
4658
|
+
return;
|
|
4659
|
+
}
|
|
4660
|
+
console.log("");
|
|
4661
|
+
console.log(` ${symbols.rocket} ${theme.primary("Installing plugin...")}`);
|
|
4662
|
+
const sourceType = detectSourceType(source);
|
|
4663
|
+
if (sourceType === "unknown") {
|
|
4664
|
+
console.log(theme.error(` ${symbols.cross} Could not determine source type for: ${source}`));
|
|
4665
|
+
return;
|
|
4666
|
+
}
|
|
4667
|
+
console.log(theme.dim(` Source type: ${sourceType}`));
|
|
4668
|
+
let result;
|
|
4669
|
+
switch (sourceType) {
|
|
4670
|
+
case "local":
|
|
4671
|
+
result = await installFromLocal(source);
|
|
4672
|
+
break;
|
|
4673
|
+
case "npm":
|
|
4674
|
+
result = await installFromNpm(source);
|
|
4675
|
+
break;
|
|
4676
|
+
case "github":
|
|
4677
|
+
result = await installFromGithub(source);
|
|
4678
|
+
break;
|
|
4679
|
+
case "git":
|
|
4680
|
+
result = await installFromGit(source);
|
|
4681
|
+
break;
|
|
4682
|
+
default:
|
|
4683
|
+
result = { success: false, error: "Unknown source type" };
|
|
4684
|
+
}
|
|
4685
|
+
if (!result.success) {
|
|
4686
|
+
console.log(theme.error(` ${symbols.cross} Installation failed: ${result.error}`));
|
|
4687
|
+
return;
|
|
4688
|
+
}
|
|
4689
|
+
const manifest = result.manifest;
|
|
4690
|
+
const conflicts = checkConflicts(manifest);
|
|
4691
|
+
if (conflicts.hasConflicts) {
|
|
4692
|
+
console.log("");
|
|
4693
|
+
console.log(theme.warning(` ${symbols.warning} Command conflicts detected:`));
|
|
4694
|
+
for (const conflict of conflicts.conflicts) {
|
|
4695
|
+
console.log(` ${theme.dim("-")} ${conflict}`);
|
|
4696
|
+
}
|
|
4697
|
+
console.log("");
|
|
4698
|
+
const proceed = await confirm(` ${theme.warning("Continue anyway?")}`);
|
|
4699
|
+
if (!proceed) {
|
|
4700
|
+
console.log(theme.dim(" Installation cancelled"));
|
|
4701
|
+
return;
|
|
4702
|
+
}
|
|
4703
|
+
}
|
|
4704
|
+
const permissions = formatPermissions(manifest);
|
|
4705
|
+
if (permissions.length > 0) {
|
|
4706
|
+
console.log("");
|
|
4707
|
+
console.log(theme.warning(` ${symbols.warning} Plugin requests permissions:`));
|
|
4708
|
+
for (const perm of permissions) {
|
|
4709
|
+
console.log(` ${perm}`);
|
|
4710
|
+
}
|
|
4711
|
+
console.log("");
|
|
4712
|
+
}
|
|
4713
|
+
await discoverPlugins();
|
|
4714
|
+
for (const cmdName of manifest.commands) {
|
|
4715
|
+
if (getCommand(cmdName)) continue;
|
|
4716
|
+
const lazyExecute = async (args3) => {
|
|
4717
|
+
const loaded = await loadPlugin(manifest.name);
|
|
4718
|
+
if (!loaded) {
|
|
4719
|
+
console.log(theme.error(` ${symbols.cross} Failed to load plugin '${manifest.name}'`));
|
|
4720
|
+
return;
|
|
4721
|
+
}
|
|
4722
|
+
const realCommand = getCommand(cmdName);
|
|
4723
|
+
if (realCommand && realCommand.execute !== lazyExecute) {
|
|
4724
|
+
await realCommand.execute(args3);
|
|
4725
|
+
} else {
|
|
4726
|
+
console.log(theme.error(` ${symbols.cross} Plugin '${manifest.name}' did not register command '${cmdName}'`));
|
|
4727
|
+
}
|
|
4728
|
+
};
|
|
4729
|
+
registerPluginCommand(
|
|
4730
|
+
{
|
|
4731
|
+
name: cmdName,
|
|
4732
|
+
description: `[${manifest.displayName || manifest.name}] ${manifest.description || "Plugin command"}`,
|
|
4733
|
+
usage: `/${cmdName}`,
|
|
4734
|
+
execute: lazyExecute
|
|
4735
|
+
},
|
|
4736
|
+
manifest.name
|
|
4737
|
+
);
|
|
4738
|
+
}
|
|
4739
|
+
console.log("");
|
|
4740
|
+
console.log(` ${symbols.check} ${theme.success("Plugin installed successfully!")}`);
|
|
4741
|
+
console.log("");
|
|
4742
|
+
console.log(` ${theme.primary(manifest.displayName || manifest.name)} ${theme.dim(`v${manifest.version}`)}`);
|
|
4743
|
+
if (manifest.description) {
|
|
4744
|
+
console.log(` ${theme.dim(manifest.description)}`);
|
|
4745
|
+
}
|
|
4746
|
+
console.log("");
|
|
4747
|
+
console.log(` ${theme.dim("Commands added:")} ${manifest.commands.map((c) => theme.accent("/" + c)).join(", ")}`);
|
|
4748
|
+
console.log("");
|
|
4749
|
+
}
|
|
4750
|
+
|
|
4751
|
+
// src/commands/plugin/remove.ts
|
|
4752
|
+
import * as readline2 from "readline";
|
|
4753
|
+
async function confirm2(message) {
|
|
4754
|
+
const rl = readline2.createInterface({
|
|
4755
|
+
input: process.stdin,
|
|
4756
|
+
output: process.stdout
|
|
4757
|
+
});
|
|
4758
|
+
return new Promise((resolve6) => {
|
|
4759
|
+
rl.question(`${message} [y/N] `, (answer) => {
|
|
4760
|
+
rl.close();
|
|
4761
|
+
resolve6(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
|
|
4762
|
+
});
|
|
4763
|
+
});
|
|
4764
|
+
}
|
|
4765
|
+
async function removePluginCommand(args2) {
|
|
4766
|
+
const forceIndex = args2.findIndex((a) => a === "-y" || a === "--yes");
|
|
4767
|
+
const skipConfirm = forceIndex !== -1;
|
|
4768
|
+
if (skipConfirm) {
|
|
4769
|
+
args2.splice(forceIndex, 1);
|
|
4770
|
+
}
|
|
4771
|
+
const name = args2[0];
|
|
4772
|
+
if (!name) {
|
|
4773
|
+
console.log(theme.error(` ${symbols.cross} No plugin name specified`));
|
|
4774
|
+
console.log("");
|
|
4775
|
+
console.log(` ${theme.primary("Usage:")} /plugin remove <name>`);
|
|
4776
|
+
console.log("");
|
|
4777
|
+
await discoverPlugins();
|
|
4778
|
+
const plugins2 = getDiscoveredPlugins();
|
|
4779
|
+
if (plugins2.length > 0) {
|
|
4780
|
+
console.log(` ${theme.dim("Installed plugins:")}`);
|
|
4781
|
+
for (const p of plugins2) {
|
|
4782
|
+
console.log(` ${theme.accent(p.name)}`);
|
|
4783
|
+
}
|
|
4784
|
+
console.log("");
|
|
4785
|
+
}
|
|
4786
|
+
return;
|
|
4787
|
+
}
|
|
4788
|
+
await discoverPlugins();
|
|
4789
|
+
const plugins = getDiscoveredPlugins();
|
|
4790
|
+
const plugin = plugins.find((p) => p.name === name || p.displayName?.toLowerCase() === name.toLowerCase());
|
|
4791
|
+
if (!plugin) {
|
|
4792
|
+
console.log(theme.error(` ${symbols.cross} Plugin '${name}' not found`));
|
|
4793
|
+
console.log("");
|
|
4794
|
+
const similar = plugins.filter(
|
|
4795
|
+
(p) => p.name.includes(name) || name.includes(p.name) || p.displayName?.toLowerCase().includes(name.toLowerCase())
|
|
4796
|
+
);
|
|
4797
|
+
if (similar.length > 0) {
|
|
4798
|
+
console.log(` ${theme.dim("Did you mean:")}`);
|
|
4799
|
+
for (const p of similar) {
|
|
4800
|
+
console.log(` ${theme.accent(p.name)}`);
|
|
4801
|
+
}
|
|
4802
|
+
console.log("");
|
|
4803
|
+
}
|
|
4804
|
+
return;
|
|
4805
|
+
}
|
|
4806
|
+
console.log("");
|
|
4807
|
+
console.log(` ${theme.warning("About to remove:")}`);
|
|
4808
|
+
console.log(` ${theme.primary(plugin.displayName || plugin.name)} ${theme.dim(`v${plugin.version}`)}`);
|
|
4809
|
+
console.log(` ${theme.dim("Commands:")} ${plugin.commands.map((c) => "/" + c).join(", ")}`);
|
|
4810
|
+
console.log("");
|
|
4811
|
+
if (!skipConfirm) {
|
|
4812
|
+
const proceed = await confirm2(` ${theme.warning("Remove this plugin?")}`);
|
|
4813
|
+
if (!proceed) {
|
|
4814
|
+
console.log(theme.dim(" Removal cancelled"));
|
|
4815
|
+
return;
|
|
4816
|
+
}
|
|
4817
|
+
}
|
|
4818
|
+
await unloadPlugin(plugin.name);
|
|
4819
|
+
const result = removePlugin(plugin.name);
|
|
4820
|
+
if (!result.success) {
|
|
4821
|
+
console.log(theme.error(` ${symbols.cross} Failed to remove: ${result.error}`));
|
|
4822
|
+
return;
|
|
4823
|
+
}
|
|
4824
|
+
await discoverPlugins();
|
|
4825
|
+
console.log("");
|
|
4826
|
+
console.log(` ${symbols.check} ${theme.success("Plugin removed successfully")}`);
|
|
4827
|
+
console.log("");
|
|
4828
|
+
}
|
|
2284
4829
|
|
|
2285
|
-
// src/
|
|
2286
|
-
import {
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
4830
|
+
// src/commands/plugin/create.ts
|
|
4831
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync4, writeFileSync as writeFileSync5 } from "fs";
|
|
4832
|
+
import { join as join8, resolve as resolve4 } from "path";
|
|
4833
|
+
import * as readline3 from "readline";
|
|
4834
|
+
async function prompt(message, defaultValue) {
|
|
4835
|
+
const rl = readline3.createInterface({
|
|
4836
|
+
input: process.stdin,
|
|
4837
|
+
output: process.stdout
|
|
4838
|
+
});
|
|
4839
|
+
const suffix = defaultValue ? ` ${theme.dim(`(${defaultValue})`)}` : "";
|
|
4840
|
+
return new Promise((resolvePromise) => {
|
|
4841
|
+
rl.question(` ${message}${suffix}: `, (answer) => {
|
|
4842
|
+
rl.close();
|
|
4843
|
+
resolvePromise(answer.trim() || defaultValue || "");
|
|
4844
|
+
});
|
|
4845
|
+
});
|
|
4846
|
+
}
|
|
4847
|
+
function generateManifest(name, displayName, description, commandName) {
|
|
4848
|
+
return JSON.stringify({
|
|
4849
|
+
name,
|
|
4850
|
+
version: "1.0.0",
|
|
4851
|
+
displayName,
|
|
4852
|
+
description,
|
|
4853
|
+
main: "./dist/index.js",
|
|
4854
|
+
commands: [commandName],
|
|
4855
|
+
zammy: {
|
|
4856
|
+
minVersion: "1.3.0"
|
|
4857
|
+
},
|
|
4858
|
+
permissions: {}
|
|
4859
|
+
}, null, 2);
|
|
4860
|
+
}
|
|
4861
|
+
function generatePackageJson(name, description) {
|
|
4862
|
+
return JSON.stringify({
|
|
4863
|
+
name,
|
|
4864
|
+
version: "1.0.0",
|
|
4865
|
+
description,
|
|
4866
|
+
type: "module",
|
|
4867
|
+
main: "dist/index.js",
|
|
4868
|
+
scripts: {
|
|
4869
|
+
build: "tsc",
|
|
4870
|
+
dev: "tsc --watch"
|
|
4871
|
+
},
|
|
4872
|
+
keywords: ["zammy-plugin"],
|
|
4873
|
+
devDependencies: {
|
|
4874
|
+
typescript: "^5.3.0"
|
|
4875
|
+
}
|
|
4876
|
+
}, null, 2);
|
|
4877
|
+
}
|
|
4878
|
+
function generateTsConfig() {
|
|
4879
|
+
return JSON.stringify({
|
|
4880
|
+
compilerOptions: {
|
|
4881
|
+
target: "ES2022",
|
|
4882
|
+
module: "NodeNext",
|
|
4883
|
+
moduleResolution: "NodeNext",
|
|
4884
|
+
outDir: "./dist",
|
|
4885
|
+
rootDir: "./src",
|
|
4886
|
+
strict: true,
|
|
4887
|
+
esModuleInterop: true,
|
|
4888
|
+
skipLibCheck: true,
|
|
4889
|
+
declaration: true
|
|
4890
|
+
},
|
|
4891
|
+
include: ["src/**/*"]
|
|
4892
|
+
}, null, 2);
|
|
4893
|
+
}
|
|
4894
|
+
function generateEntryPoint(commandName, displayName) {
|
|
4895
|
+
return `// ${displayName} - A zammy plugin
|
|
4896
|
+
|
|
4897
|
+
// Plugin types (these match zammy's PluginAPI interface)
|
|
4898
|
+
interface Command {
|
|
4899
|
+
name: string;
|
|
4900
|
+
description: string;
|
|
4901
|
+
usage: string;
|
|
4902
|
+
execute: (args: string[]) => Promise<void>;
|
|
4903
|
+
}
|
|
4904
|
+
|
|
4905
|
+
interface PluginAPI {
|
|
4906
|
+
registerCommand(command: Command): void;
|
|
4907
|
+
ui: {
|
|
4908
|
+
theme: {
|
|
4909
|
+
primary: (text: string) => string;
|
|
4910
|
+
secondary: (text: string) => string;
|
|
4911
|
+
accent: (text: string) => string;
|
|
4912
|
+
success: (text: string) => string;
|
|
4913
|
+
warning: (text: string) => string;
|
|
4914
|
+
error: (text: string) => string;
|
|
4915
|
+
info: (text: string) => string;
|
|
4916
|
+
dim: (text: string) => string;
|
|
4917
|
+
gradient: (text: string) => string;
|
|
4918
|
+
};
|
|
4919
|
+
symbols: {
|
|
4920
|
+
check: string;
|
|
4921
|
+
cross: string;
|
|
4922
|
+
star: string;
|
|
4923
|
+
arrow: string;
|
|
4924
|
+
bullet: string;
|
|
4925
|
+
folder: string;
|
|
4926
|
+
file: string;
|
|
4927
|
+
warning: string;
|
|
4928
|
+
info: string;
|
|
4929
|
+
rocket: string;
|
|
4930
|
+
sparkles: string;
|
|
4931
|
+
};
|
|
4932
|
+
};
|
|
4933
|
+
storage: {
|
|
4934
|
+
get<T>(key: string): T | undefined;
|
|
4935
|
+
set<T>(key: string, value: T): void;
|
|
4936
|
+
delete(key: string): void;
|
|
4937
|
+
};
|
|
4938
|
+
log: {
|
|
4939
|
+
info(message: string): void;
|
|
4940
|
+
warn(message: string): void;
|
|
4941
|
+
error(message: string): void;
|
|
4942
|
+
};
|
|
4943
|
+
context: {
|
|
4944
|
+
pluginName: string;
|
|
4945
|
+
pluginVersion: string;
|
|
4946
|
+
zammyVersion: string;
|
|
4947
|
+
dataDir: string;
|
|
4948
|
+
cwd: string;
|
|
2296
4949
|
};
|
|
2297
4950
|
}
|
|
2298
4951
|
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2306
|
-
const
|
|
4952
|
+
interface ZammyPlugin {
|
|
4953
|
+
activate(api: PluginAPI): Promise<void> | void;
|
|
4954
|
+
deactivate?(): Promise<void> | void;
|
|
4955
|
+
}
|
|
4956
|
+
|
|
4957
|
+
const plugin: ZammyPlugin = {
|
|
4958
|
+
activate(api: PluginAPI) {
|
|
4959
|
+
const { theme, symbols } = api.ui;
|
|
4960
|
+
|
|
4961
|
+
api.registerCommand({
|
|
4962
|
+
name: '${commandName}',
|
|
4963
|
+
description: 'My custom command',
|
|
4964
|
+
usage: '/${commandName} [args]',
|
|
4965
|
+
async execute(args: string[]) {
|
|
4966
|
+
console.log('');
|
|
4967
|
+
console.log(\` \${symbols.star} \${theme.gradient('${displayName.toUpperCase()}')}\`);
|
|
4968
|
+
console.log('');
|
|
4969
|
+
console.log(\` \${theme.success('Hello from ${displayName}!')}\`);
|
|
4970
|
+
|
|
4971
|
+
if (args.length > 0) {
|
|
4972
|
+
console.log(\` \${theme.dim('Arguments:')} \${args.join(' ')}\`);
|
|
4973
|
+
}
|
|
4974
|
+
|
|
4975
|
+
console.log('');
|
|
4976
|
+
},
|
|
4977
|
+
});
|
|
4978
|
+
|
|
4979
|
+
api.log.info('Plugin activated!');
|
|
4980
|
+
},
|
|
4981
|
+
|
|
4982
|
+
deactivate() {
|
|
4983
|
+
// Cleanup if needed
|
|
4984
|
+
},
|
|
4985
|
+
};
|
|
4986
|
+
|
|
4987
|
+
export default plugin;
|
|
4988
|
+
`;
|
|
4989
|
+
}
|
|
4990
|
+
function generateReadme(name, displayName, description, commandName) {
|
|
4991
|
+
return `# ${displayName}
|
|
4992
|
+
|
|
4993
|
+
${description}
|
|
4994
|
+
|
|
4995
|
+
## Installation
|
|
4996
|
+
|
|
4997
|
+
\`\`\`bash
|
|
4998
|
+
/plugin install ./${name}
|
|
4999
|
+
\`\`\`
|
|
5000
|
+
|
|
5001
|
+
## Usage
|
|
5002
|
+
|
|
5003
|
+
\`\`\`bash
|
|
5004
|
+
/${commandName} [args]
|
|
5005
|
+
\`\`\`
|
|
5006
|
+
|
|
5007
|
+
## Development
|
|
5008
|
+
|
|
5009
|
+
\`\`\`bash
|
|
5010
|
+
# Build the plugin
|
|
5011
|
+
npm run build
|
|
5012
|
+
|
|
5013
|
+
# Watch for changes
|
|
5014
|
+
npm run dev
|
|
5015
|
+
\`\`\`
|
|
5016
|
+
|
|
5017
|
+
## License
|
|
5018
|
+
|
|
5019
|
+
MIT
|
|
5020
|
+
`;
|
|
5021
|
+
}
|
|
5022
|
+
async function createPlugin(args2) {
|
|
5023
|
+
console.log("");
|
|
5024
|
+
console.log(` ${symbols.sparkle} ${theme.gradient("CREATE NEW PLUGIN")}`);
|
|
5025
|
+
console.log("");
|
|
5026
|
+
let name = args2[0] || "";
|
|
5027
|
+
if (!name) {
|
|
5028
|
+
name = await prompt("Plugin name (e.g., my-plugin)", "zammy-plugin-example");
|
|
5029
|
+
}
|
|
5030
|
+
name = name.toLowerCase().replace(/\s+/g, "-");
|
|
5031
|
+
if (!name.startsWith("zammy-plugin-")) {
|
|
5032
|
+
}
|
|
5033
|
+
const displayName = await prompt("Display name", name.replace(/^zammy-plugin-/, "").replace(/-/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()));
|
|
5034
|
+
const description = await prompt("Description", "A zammy plugin");
|
|
5035
|
+
const commandName = await prompt("Main command name", name.replace(/^zammy-plugin-/, "").replace(/-/g, ""));
|
|
5036
|
+
const targetDir = resolve4(process.cwd(), name);
|
|
5037
|
+
if (existsSync10(targetDir)) {
|
|
5038
|
+
console.log(theme.error(` ${symbols.cross} Directory already exists: ${name}`));
|
|
5039
|
+
return;
|
|
5040
|
+
}
|
|
5041
|
+
console.log("");
|
|
5042
|
+
console.log(theme.dim(` Creating plugin in ${targetDir}...`));
|
|
5043
|
+
try {
|
|
5044
|
+
mkdirSync4(targetDir);
|
|
5045
|
+
mkdirSync4(join8(targetDir, "src"));
|
|
5046
|
+
mkdirSync4(join8(targetDir, "dist"));
|
|
5047
|
+
writeFileSync5(join8(targetDir, "zammy-plugin.json"), generateManifest(name, displayName, description, commandName));
|
|
5048
|
+
writeFileSync5(join8(targetDir, "package.json"), generatePackageJson(name, description));
|
|
5049
|
+
writeFileSync5(join8(targetDir, "tsconfig.json"), generateTsConfig());
|
|
5050
|
+
writeFileSync5(join8(targetDir, "src", "index.ts"), generateEntryPoint(commandName, displayName));
|
|
5051
|
+
writeFileSync5(join8(targetDir, "README.md"), generateReadme(name, displayName, description, commandName));
|
|
2307
5052
|
console.log("");
|
|
2308
|
-
console.log(` ${symbols.
|
|
5053
|
+
console.log(` ${symbols.check} ${theme.success("Plugin created successfully!")}`);
|
|
2309
5054
|
console.log("");
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
});
|
|
5055
|
+
console.log(` ${theme.primary("Next steps:")}`);
|
|
5056
|
+
console.log(` ${theme.dim("1.")} cd ${name}`);
|
|
5057
|
+
console.log(` ${theme.dim("2.")} npm install`);
|
|
5058
|
+
console.log(` ${theme.dim("3.")} npm run build`);
|
|
5059
|
+
console.log(` ${theme.dim("4.")} /plugin install ./${name}`);
|
|
2313
5060
|
console.log("");
|
|
2314
|
-
|
|
2315
|
-
console.log(theme.dim(" Tip: /uuid 5 generates 5 UUIDs"));
|
|
2316
|
-
}
|
|
5061
|
+
console.log(` ${theme.dim("Edit")} ${theme.accent("src/index.ts")} ${theme.dim("to customize your plugin")}`);
|
|
2317
5062
|
console.log("");
|
|
5063
|
+
} catch (error) {
|
|
5064
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5065
|
+
console.log(theme.error(` ${symbols.cross} Failed to create plugin: ${message}`));
|
|
2318
5066
|
}
|
|
2319
|
-
});
|
|
2320
|
-
|
|
2321
|
-
// src/handlers/dev/encode.ts
|
|
2322
|
-
var SUPPORTED_METHODS = ["base64", "url", "hex"];
|
|
2323
|
-
function isValidMethod(method) {
|
|
2324
|
-
return SUPPORTED_METHODS.includes(method.toLowerCase());
|
|
2325
|
-
}
|
|
2326
|
-
function encodeText(text, method, direction) {
|
|
2327
|
-
let output;
|
|
2328
|
-
if (direction === "encode") {
|
|
2329
|
-
switch (method) {
|
|
2330
|
-
case "base64":
|
|
2331
|
-
output = Buffer.from(text).toString("base64");
|
|
2332
|
-
break;
|
|
2333
|
-
case "url":
|
|
2334
|
-
output = encodeURIComponent(text);
|
|
2335
|
-
break;
|
|
2336
|
-
case "hex":
|
|
2337
|
-
output = Buffer.from(text).toString("hex");
|
|
2338
|
-
break;
|
|
2339
|
-
}
|
|
2340
|
-
} else {
|
|
2341
|
-
switch (method) {
|
|
2342
|
-
case "base64":
|
|
2343
|
-
output = Buffer.from(text, "base64").toString("utf-8");
|
|
2344
|
-
break;
|
|
2345
|
-
case "url":
|
|
2346
|
-
output = decodeURIComponent(text);
|
|
2347
|
-
break;
|
|
2348
|
-
case "hex":
|
|
2349
|
-
output = Buffer.from(text, "hex").toString("utf-8");
|
|
2350
|
-
break;
|
|
2351
|
-
}
|
|
2352
|
-
}
|
|
2353
|
-
return {
|
|
2354
|
-
method: method.toUpperCase(),
|
|
2355
|
-
direction,
|
|
2356
|
-
input: text,
|
|
2357
|
-
output
|
|
2358
|
-
};
|
|
2359
5067
|
}
|
|
2360
5068
|
|
|
2361
|
-
// src/commands/
|
|
5069
|
+
// src/commands/plugin/index.ts
|
|
2362
5070
|
registerCommand({
|
|
2363
|
-
name: "
|
|
2364
|
-
description: "
|
|
2365
|
-
usage: "/
|
|
5071
|
+
name: "plugin",
|
|
5072
|
+
description: "Manage zammy plugins",
|
|
5073
|
+
usage: "/plugin <list|install|remove|create> [args]",
|
|
2366
5074
|
async execute(args2) {
|
|
2367
|
-
|
|
5075
|
+
const subcommand = args2[0]?.toLowerCase();
|
|
5076
|
+
if (!subcommand || subcommand === "help") {
|
|
2368
5077
|
console.log("");
|
|
2369
|
-
console.log(theme.
|
|
2370
|
-
console.log(theme.dim(` Methods: ${SUPPORTED_METHODS.join(", ")}`));
|
|
2371
|
-
console.log(theme.dim(" Example: /encode base64 encode hello"));
|
|
2372
|
-
console.log(theme.dim(" Example: /encode url decode hello%20world"));
|
|
2373
|
-
console.log("");
|
|
2374
|
-
return;
|
|
2375
|
-
}
|
|
2376
|
-
const method = args2[0].toLowerCase();
|
|
2377
|
-
const action = args2[1].toLowerCase();
|
|
2378
|
-
const text = args2.slice(2).join(" ");
|
|
2379
|
-
if (!isValidMethod(method)) {
|
|
2380
|
-
console.log(theme.error(`Unknown method: ${method}. Use ${SUPPORTED_METHODS.join(", ")}`));
|
|
2381
|
-
return;
|
|
2382
|
-
}
|
|
2383
|
-
if (action !== "encode" && action !== "decode") {
|
|
2384
|
-
console.log(theme.error(`Unknown action: ${action}. Use encode or decode`));
|
|
2385
|
-
return;
|
|
2386
|
-
}
|
|
2387
|
-
if (!text) {
|
|
2388
|
-
console.log(theme.error("Please provide text to encode/decode"));
|
|
2389
|
-
return;
|
|
2390
|
-
}
|
|
2391
|
-
try {
|
|
2392
|
-
const result = encodeText(text, method, action);
|
|
2393
|
-
console.log("");
|
|
2394
|
-
console.log(box.draw([
|
|
2395
|
-
"",
|
|
2396
|
-
` ${symbols.sparkle} ${theme.gradient(result.method + " " + result.direction.toUpperCase())}`,
|
|
2397
|
-
"",
|
|
2398
|
-
` ${theme.dim("Input:")}`,
|
|
2399
|
-
` ${theme.secondary(result.input.length > 50 ? result.input.slice(0, 50) + "..." : result.input)}`,
|
|
2400
|
-
"",
|
|
2401
|
-
` ${theme.dim("Output:")}`,
|
|
2402
|
-
` ${theme.success(result.output.length > 50 ? result.output.slice(0, 50) + "..." : result.output)}`,
|
|
2403
|
-
"",
|
|
2404
|
-
...result.output.length > 50 ? [` ${theme.dim("(Full output: " + result.output.length + " chars)")}`] : [],
|
|
2405
|
-
""
|
|
2406
|
-
], 60));
|
|
5078
|
+
console.log(` ${symbols.gear} ${theme.gradient("PLUGIN MANAGER")}`);
|
|
2407
5079
|
console.log("");
|
|
2408
|
-
|
|
2409
|
-
console.log(theme.dim(" Full result:"));
|
|
2410
|
-
console.log(` ${result.output}`);
|
|
2411
|
-
console.log("");
|
|
2412
|
-
}
|
|
2413
|
-
} catch (e) {
|
|
2414
|
-
console.log(theme.error(`Failed to ${action}: Invalid input for ${method}`));
|
|
2415
|
-
}
|
|
2416
|
-
}
|
|
2417
|
-
});
|
|
2418
|
-
|
|
2419
|
-
// src/commands/info/weather.ts
|
|
2420
|
-
registerCommand({
|
|
2421
|
-
name: "weather",
|
|
2422
|
-
description: "Get current weather for a city",
|
|
2423
|
-
usage: "/weather <city>",
|
|
2424
|
-
async execute(args2) {
|
|
2425
|
-
const city = args2.join(" ") || "London";
|
|
2426
|
-
console.log(theme.dim(`Fetching weather for ${city}...`));
|
|
2427
|
-
try {
|
|
2428
|
-
const response = await fetch(`https://wttr.in/${encodeURIComponent(city)}?format=j1`);
|
|
2429
|
-
if (!response.ok) {
|
|
2430
|
-
console.log(theme.error(`Could not fetch weather for "${city}"`));
|
|
2431
|
-
return;
|
|
2432
|
-
}
|
|
2433
|
-
const data = await response.json();
|
|
2434
|
-
const current = data.current_condition[0];
|
|
2435
|
-
const location = data.nearest_area[0];
|
|
2436
|
-
const temp = current.temp_C;
|
|
2437
|
-
const feelsLike = current.FeelsLikeC;
|
|
2438
|
-
const desc = current.weatherDesc[0].value;
|
|
2439
|
-
const humidity = current.humidity;
|
|
2440
|
-
const wind = current.windspeedKmph;
|
|
2441
|
-
const cityName = location.areaName[0].value;
|
|
2442
|
-
const country = location.country[0].value;
|
|
5080
|
+
console.log(` ${theme.primary("Usage:")} /plugin <command> [args]`);
|
|
2443
5081
|
console.log("");
|
|
2444
|
-
console.log(theme.
|
|
5082
|
+
console.log(` ${theme.primary("Commands:")}`);
|
|
5083
|
+
console.log(` ${theme.accent("list")} ${theme.dim("Show installed plugins")}`);
|
|
5084
|
+
console.log(` ${theme.accent("install")} <source> ${theme.dim("Install a plugin")}`);
|
|
5085
|
+
console.log(` ${theme.accent("remove")} <name> ${theme.dim("Remove a plugin")}`);
|
|
5086
|
+
console.log(` ${theme.accent("create")} [name] ${theme.dim("Create a new plugin")}`);
|
|
2445
5087
|
console.log("");
|
|
2446
|
-
console.log(` ${theme.primary(
|
|
2447
|
-
console.log(`
|
|
2448
|
-
console.log(`
|
|
2449
|
-
console.log(`
|
|
5088
|
+
console.log(` ${theme.primary("Install sources:")}`);
|
|
5089
|
+
console.log(` ${theme.dim("./path/to/plugin")} ${theme.dim("Local directory")}`);
|
|
5090
|
+
console.log(` ${theme.dim("package-name")} ${theme.dim("npm package")}`);
|
|
5091
|
+
console.log(` ${theme.dim("github:user/repo")} ${theme.dim("GitHub repository")}`);
|
|
5092
|
+
console.log(` ${theme.dim("https://...git")} ${theme.dim("Git URL")}`);
|
|
2450
5093
|
console.log("");
|
|
2451
|
-
|
|
2452
|
-
|
|
5094
|
+
return;
|
|
5095
|
+
}
|
|
5096
|
+
switch (subcommand) {
|
|
5097
|
+
case "list":
|
|
5098
|
+
case "ls":
|
|
5099
|
+
await listPlugins();
|
|
5100
|
+
break;
|
|
5101
|
+
case "install":
|
|
5102
|
+
case "i":
|
|
5103
|
+
case "add":
|
|
5104
|
+
await installPlugin(args2.slice(1));
|
|
5105
|
+
break;
|
|
5106
|
+
case "remove":
|
|
5107
|
+
case "rm":
|
|
5108
|
+
case "uninstall":
|
|
5109
|
+
await removePluginCommand(args2.slice(1));
|
|
5110
|
+
break;
|
|
5111
|
+
case "create":
|
|
5112
|
+
case "new":
|
|
5113
|
+
case "init":
|
|
5114
|
+
await createPlugin(args2.slice(1));
|
|
5115
|
+
break;
|
|
5116
|
+
default:
|
|
5117
|
+
console.log(theme.error(` ${symbols.cross} Unknown subcommand: ${subcommand}`));
|
|
5118
|
+
console.log(theme.dim(` Use '/plugin help' to see available commands`));
|
|
2453
5119
|
}
|
|
2454
5120
|
}
|
|
2455
5121
|
});
|
|
2456
5122
|
|
|
2457
5123
|
// src/cli.ts
|
|
2458
|
-
import { exec, execSync, spawn } from "child_process";
|
|
2459
|
-
import { existsSync as
|
|
2460
|
-
import { resolve as
|
|
2461
|
-
import { homedir as
|
|
2462
|
-
import
|
|
2463
|
-
var
|
|
5124
|
+
import { exec, execSync as execSync3, spawn } from "child_process";
|
|
5125
|
+
import { existsSync as existsSync11, statSync as statSync2, readFileSync as readFileSync9, readdirSync as readdirSync4, writeFileSync as writeFileSync6, watchFile, unwatchFile } from "fs";
|
|
5126
|
+
import { resolve as resolve5, extname, basename as basename4, join as join9 } from "path";
|
|
5127
|
+
import { homedir as homedir5, platform as platform3, networkInterfaces } from "os";
|
|
5128
|
+
import chalk5 from "chalk";
|
|
5129
|
+
var isWindows2 = platform3() === "win32";
|
|
2464
5130
|
function translateCommand(cmd) {
|
|
2465
|
-
if (!
|
|
5131
|
+
if (!isWindows2) return cmd;
|
|
2466
5132
|
const parts = cmd.trim().split(/\s+/);
|
|
2467
5133
|
const command = parts[0].toLowerCase();
|
|
2468
5134
|
const args2 = parts.slice(1);
|
|
@@ -2514,42 +5180,42 @@ function translateCommand(cmd) {
|
|
|
2514
5180
|
function handleCd(args2) {
|
|
2515
5181
|
let targetPath = args2.trim();
|
|
2516
5182
|
if (!targetPath || targetPath === "~") {
|
|
2517
|
-
targetPath =
|
|
5183
|
+
targetPath = homedir5();
|
|
2518
5184
|
} else if (targetPath.startsWith("~/")) {
|
|
2519
|
-
targetPath =
|
|
5185
|
+
targetPath = resolve5(homedir5(), targetPath.slice(2));
|
|
2520
5186
|
} else if (targetPath === "-") {
|
|
2521
5187
|
console.log(theme.dim(process.cwd()));
|
|
2522
5188
|
return;
|
|
2523
5189
|
} else {
|
|
2524
|
-
targetPath =
|
|
5190
|
+
targetPath = resolve5(process.cwd(), targetPath);
|
|
2525
5191
|
}
|
|
2526
|
-
if (!
|
|
2527
|
-
console.log(theme.error(`Directory not found: ${targetPath}`));
|
|
5192
|
+
if (!existsSync11(targetPath)) {
|
|
5193
|
+
console.log(`${miniSlime.sad} ${theme.error(`Directory not found: ${targetPath}`)}`);
|
|
2528
5194
|
return;
|
|
2529
5195
|
}
|
|
2530
5196
|
try {
|
|
2531
|
-
const stats =
|
|
5197
|
+
const stats = statSync2(targetPath);
|
|
2532
5198
|
if (!stats.isDirectory()) {
|
|
2533
|
-
console.log(theme.error(`Not a directory: ${targetPath}`));
|
|
5199
|
+
console.log(`${miniSlime.sad} ${theme.error(`Not a directory: ${targetPath}`)}`);
|
|
2534
5200
|
return;
|
|
2535
5201
|
}
|
|
2536
5202
|
process.chdir(targetPath);
|
|
2537
5203
|
console.log(theme.dim(process.cwd()));
|
|
2538
5204
|
} catch (error) {
|
|
2539
|
-
console.log(theme.error(`Cannot access: ${targetPath}`));
|
|
5205
|
+
console.log(`${miniSlime.sad} ${theme.error(`Cannot access: ${targetPath}`)}`);
|
|
2540
5206
|
}
|
|
2541
5207
|
}
|
|
2542
5208
|
function handlePwd() {
|
|
2543
5209
|
console.log(theme.primary(process.cwd()));
|
|
2544
5210
|
}
|
|
2545
5211
|
function handleCat(args2) {
|
|
2546
|
-
const filePath =
|
|
2547
|
-
if (!
|
|
2548
|
-
console.log(theme.error(`File not found: ${args2}`));
|
|
5212
|
+
const filePath = resolve5(process.cwd(), args2.trim());
|
|
5213
|
+
if (!existsSync11(filePath)) {
|
|
5214
|
+
console.log(`${miniSlime.sad} ${theme.error(`File not found: ${args2}`)}`);
|
|
2549
5215
|
return;
|
|
2550
5216
|
}
|
|
2551
5217
|
try {
|
|
2552
|
-
const content =
|
|
5218
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
2553
5219
|
const ext = extname(filePath).toLowerCase();
|
|
2554
5220
|
if ([".js", ".ts", ".jsx", ".tsx", ".json", ".css", ".html", ".py", ".go", ".rs"].includes(ext)) {
|
|
2555
5221
|
console.log(highlightSyntax(content, ext));
|
|
@@ -2557,7 +5223,7 @@ function handleCat(args2) {
|
|
|
2557
5223
|
console.log(content);
|
|
2558
5224
|
}
|
|
2559
5225
|
} catch (error) {
|
|
2560
|
-
console.log(theme.error(`Cannot read file: ${args2}`));
|
|
5226
|
+
console.log(`${miniSlime.sad} ${theme.error(`Cannot read file: ${args2}`)}`);
|
|
2561
5227
|
}
|
|
2562
5228
|
}
|
|
2563
5229
|
function highlightSyntax(content, ext) {
|
|
@@ -2567,12 +5233,12 @@ function highlightSyntax(content, ext) {
|
|
|
2567
5233
|
};
|
|
2568
5234
|
const kw = ext === ".py" ? keywords.py : keywords.js;
|
|
2569
5235
|
return content.split("\n").map((line) => {
|
|
2570
|
-
line = line.replace(/(["'`])(?:(?!\1)[^\\]|\\.)*?\1/g, (match) =>
|
|
2571
|
-
line = line.replace(/(\/\/.*$|#.*$)/g, (match) =>
|
|
2572
|
-
line = line.replace(/\b(\d+\.?\d*)\b/g, (match) =>
|
|
5236
|
+
line = line.replace(/(["'`])(?:(?!\1)[^\\]|\\.)*?\1/g, (match) => chalk5.hex("#98C379")(match));
|
|
5237
|
+
line = line.replace(/(\/\/.*$|#.*$)/g, (match) => chalk5.hex("#5C6370")(match));
|
|
5238
|
+
line = line.replace(/\b(\d+\.?\d*)\b/g, (match) => chalk5.hex("#D19A66")(match));
|
|
2573
5239
|
kw.forEach((keyword) => {
|
|
2574
5240
|
const regex = new RegExp(`\\b(${keyword})\\b`, "g");
|
|
2575
|
-
line = line.replace(regex,
|
|
5241
|
+
line = line.replace(regex, chalk5.hex("#C678DD")(keyword));
|
|
2576
5242
|
});
|
|
2577
5243
|
return line;
|
|
2578
5244
|
}).join("\n");
|
|
@@ -2637,24 +5303,24 @@ var fileIcons = {
|
|
|
2637
5303
|
"default": { icon: "\u{1F4C4}", color: "#6C7A89" }
|
|
2638
5304
|
};
|
|
2639
5305
|
function formatSize(bytes) {
|
|
2640
|
-
if (bytes === 0) return
|
|
5306
|
+
if (bytes === 0) return chalk5.dim(" 0 B");
|
|
2641
5307
|
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
2642
5308
|
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
2643
5309
|
const size = (bytes / Math.pow(1024, i)).toFixed(i > 0 ? 1 : 0);
|
|
2644
|
-
return
|
|
5310
|
+
return chalk5.hex("#98C379")(size.padStart(5) + " " + units[i].padEnd(2));
|
|
2645
5311
|
}
|
|
2646
5312
|
function handleLs(args2) {
|
|
2647
5313
|
const parts = args2.trim().split(/\s+/).filter(Boolean);
|
|
2648
5314
|
const showAll = parts.some((a) => a === "-a" || a === "-la" || a === "-al");
|
|
2649
5315
|
const showLong = parts.some((a) => a === "-l" || a === "-la" || a === "-al");
|
|
2650
5316
|
const pathArgs = parts.filter((a) => !a.startsWith("-"));
|
|
2651
|
-
const targetPath = pathArgs.length > 0 ?
|
|
2652
|
-
if (!
|
|
5317
|
+
const targetPath = pathArgs.length > 0 ? resolve5(process.cwd(), pathArgs[0]) : process.cwd();
|
|
5318
|
+
if (!existsSync11(targetPath)) {
|
|
2653
5319
|
console.log(theme.error(`Directory not found: ${targetPath}`));
|
|
2654
5320
|
return;
|
|
2655
5321
|
}
|
|
2656
5322
|
try {
|
|
2657
|
-
const entries =
|
|
5323
|
+
const entries = readdirSync4(targetPath, { withFileTypes: true });
|
|
2658
5324
|
const filtered = showAll ? entries : entries.filter((e) => !e.name.startsWith("."));
|
|
2659
5325
|
console.log("");
|
|
2660
5326
|
console.log(theme.dim(` ${targetPath}`));
|
|
@@ -2664,28 +5330,28 @@ function handleLs(args2) {
|
|
|
2664
5330
|
console.log("");
|
|
2665
5331
|
return;
|
|
2666
5332
|
}
|
|
2667
|
-
const sorted = filtered.sort((a,
|
|
2668
|
-
if (a.isDirectory() && !
|
|
2669
|
-
if (!a.isDirectory() &&
|
|
2670
|
-
return a.name.localeCompare(
|
|
5333
|
+
const sorted = filtered.sort((a, b3) => {
|
|
5334
|
+
if (a.isDirectory() && !b3.isDirectory()) return -1;
|
|
5335
|
+
if (!a.isDirectory() && b3.isDirectory()) return 1;
|
|
5336
|
+
return a.name.localeCompare(b3.name);
|
|
2671
5337
|
});
|
|
2672
5338
|
for (const entry of sorted) {
|
|
2673
|
-
const fullPath =
|
|
5339
|
+
const fullPath = resolve5(targetPath, entry.name);
|
|
2674
5340
|
const isDir = entry.isDirectory();
|
|
2675
5341
|
const ext = isDir ? "dir" : extname(entry.name).toLowerCase();
|
|
2676
5342
|
const iconInfo = fileIcons[ext] || fileIcons["default"];
|
|
2677
5343
|
let line = ` ${iconInfo.icon} `;
|
|
2678
5344
|
if (showLong) {
|
|
2679
5345
|
try {
|
|
2680
|
-
const stats =
|
|
2681
|
-
const size = isDir ?
|
|
5346
|
+
const stats = statSync2(fullPath);
|
|
5347
|
+
const size = isDir ? chalk5.dim(" <DIR>") : formatSize(stats.size);
|
|
2682
5348
|
const date = stats.mtime.toLocaleDateString("en-US", { month: "short", day: "2-digit", year: "numeric" });
|
|
2683
|
-
line +=
|
|
5349
|
+
line += chalk5.dim(date.padEnd(13)) + size + " ";
|
|
2684
5350
|
} catch {
|
|
2685
|
-
line +=
|
|
5351
|
+
line += chalk5.dim(" ") + " ";
|
|
2686
5352
|
}
|
|
2687
5353
|
}
|
|
2688
|
-
const name = isDir ?
|
|
5354
|
+
const name = isDir ? chalk5.hex(iconInfo.color).bold(entry.name + "/") : chalk5.hex(iconInfo.color)(entry.name);
|
|
2689
5355
|
line += name;
|
|
2690
5356
|
console.log(line);
|
|
2691
5357
|
}
|
|
@@ -2699,22 +5365,22 @@ function handleLs(args2) {
|
|
|
2699
5365
|
function handleTree(args2, maxDepth = 3) {
|
|
2700
5366
|
const parts = args2.trim().split(/\s+/).filter(Boolean);
|
|
2701
5367
|
const pathArgs = parts.filter((a) => !a.startsWith("-"));
|
|
2702
|
-
const targetPath = pathArgs.length > 0 ?
|
|
2703
|
-
if (!
|
|
5368
|
+
const targetPath = pathArgs.length > 0 ? resolve5(process.cwd(), pathArgs[0]) : process.cwd();
|
|
5369
|
+
if (!existsSync11(targetPath)) {
|
|
2704
5370
|
console.log(theme.error(`Directory not found: ${targetPath}`));
|
|
2705
5371
|
return;
|
|
2706
5372
|
}
|
|
2707
5373
|
console.log("");
|
|
2708
|
-
console.log(theme.primary(` \u{1F4C1} ${
|
|
5374
|
+
console.log(theme.primary(` \u{1F4C1} ${basename4(targetPath)}/`));
|
|
2709
5375
|
let dirCount = 0;
|
|
2710
5376
|
let fileCount = 0;
|
|
2711
5377
|
function printTree(dir, prefix, depth) {
|
|
2712
5378
|
if (depth > maxDepth) return;
|
|
2713
5379
|
try {
|
|
2714
|
-
const entries =
|
|
2715
|
-
if (a.isDirectory() && !
|
|
2716
|
-
if (!a.isDirectory() &&
|
|
2717
|
-
return a.name.localeCompare(
|
|
5380
|
+
const entries = readdirSync4(dir, { withFileTypes: true }).filter((e) => !e.name.startsWith(".") && !["node_modules", ".git", "dist", "build"].includes(e.name)).sort((a, b3) => {
|
|
5381
|
+
if (a.isDirectory() && !b3.isDirectory()) return -1;
|
|
5382
|
+
if (!a.isDirectory() && b3.isDirectory()) return 1;
|
|
5383
|
+
return a.name.localeCompare(b3.name);
|
|
2718
5384
|
});
|
|
2719
5385
|
entries.forEach((entry, index) => {
|
|
2720
5386
|
const isLast = index === entries.length - 1;
|
|
@@ -2723,11 +5389,11 @@ function handleTree(args2, maxDepth = 3) {
|
|
|
2723
5389
|
const iconInfo = fileIcons[ext] || fileIcons["default"];
|
|
2724
5390
|
if (entry.isDirectory()) {
|
|
2725
5391
|
dirCount++;
|
|
2726
|
-
console.log(theme.dim(prefix + connector) + iconInfo.icon + " " +
|
|
2727
|
-
printTree(
|
|
5392
|
+
console.log(theme.dim(prefix + connector) + iconInfo.icon + " " + chalk5.hex(iconInfo.color).bold(entry.name + "/"));
|
|
5393
|
+
printTree(resolve5(dir, entry.name), prefix + (isLast ? " " : "\u2502 "), depth + 1);
|
|
2728
5394
|
} else {
|
|
2729
5395
|
fileCount++;
|
|
2730
|
-
console.log(theme.dim(prefix + connector) + iconInfo.icon + " " +
|
|
5396
|
+
console.log(theme.dim(prefix + connector) + iconInfo.icon + " " + chalk5.hex(iconInfo.color)(entry.name));
|
|
2731
5397
|
}
|
|
2732
5398
|
});
|
|
2733
5399
|
} catch {
|
|
@@ -2738,18 +5404,18 @@ function handleTree(args2, maxDepth = 3) {
|
|
|
2738
5404
|
console.log(theme.dim(` ${dirCount} directories, ${fileCount} files`));
|
|
2739
5405
|
console.log("");
|
|
2740
5406
|
}
|
|
2741
|
-
var bookmarksFile =
|
|
5407
|
+
var bookmarksFile = join9(homedir5(), ".zammy-bookmarks.json");
|
|
2742
5408
|
function loadBookmarks() {
|
|
2743
5409
|
try {
|
|
2744
|
-
if (
|
|
2745
|
-
return JSON.parse(
|
|
5410
|
+
if (existsSync11(bookmarksFile)) {
|
|
5411
|
+
return JSON.parse(readFileSync9(bookmarksFile, "utf-8"));
|
|
2746
5412
|
}
|
|
2747
5413
|
} catch {
|
|
2748
5414
|
}
|
|
2749
5415
|
return {};
|
|
2750
5416
|
}
|
|
2751
5417
|
function saveBookmarks(bookmarks) {
|
|
2752
|
-
|
|
5418
|
+
writeFileSync6(bookmarksFile, JSON.stringify(bookmarks, null, 2));
|
|
2753
5419
|
}
|
|
2754
5420
|
function handleBookmark(args2) {
|
|
2755
5421
|
const parts = args2.trim().split(/\s+/);
|
|
@@ -2761,38 +5427,38 @@ function handleBookmark(args2) {
|
|
|
2761
5427
|
case "save":
|
|
2762
5428
|
case "add":
|
|
2763
5429
|
if (!name) {
|
|
2764
|
-
console.log(theme.error("
|
|
5430
|
+
console.log(` ${miniSlime.thinking} ${theme.error("Usage: bookmark save <name>")}`);
|
|
2765
5431
|
break;
|
|
2766
5432
|
}
|
|
2767
5433
|
bookmarks[name] = process.cwd();
|
|
2768
5434
|
saveBookmarks(bookmarks);
|
|
2769
|
-
console.log(` ${
|
|
5435
|
+
console.log(` ${miniSlime.happy} ${theme.success("Saved bookmark:")} ${theme.primary(name)} ${theme.dim("\u2192")} ${process.cwd()}`);
|
|
2770
5436
|
break;
|
|
2771
5437
|
case "go":
|
|
2772
5438
|
case "cd":
|
|
2773
5439
|
if (!name || !bookmarks[name]) {
|
|
2774
|
-
console.log(theme.error(`
|
|
5440
|
+
console.log(` ${miniSlime.sad} ${theme.error(`Bookmark not found: ${name}`)}`);
|
|
2775
5441
|
console.log(theme.dim(' Use "bookmark list" to see all bookmarks'));
|
|
2776
5442
|
break;
|
|
2777
5443
|
}
|
|
2778
5444
|
try {
|
|
2779
5445
|
process.chdir(bookmarks[name]);
|
|
2780
|
-
console.log(` ${
|
|
5446
|
+
console.log(` ${miniSlime.excited} ${theme.dim("Jumped to")} ${theme.primary(name)}`);
|
|
2781
5447
|
console.log(` ${theme.dim(process.cwd())}`);
|
|
2782
5448
|
} catch {
|
|
2783
|
-
console.log(theme.error(`
|
|
5449
|
+
console.log(` ${miniSlime.sad} ${theme.error(`Cannot access: ${bookmarks[name]}`)}`);
|
|
2784
5450
|
}
|
|
2785
5451
|
break;
|
|
2786
5452
|
case "del":
|
|
2787
5453
|
case "delete":
|
|
2788
5454
|
case "rm":
|
|
2789
5455
|
if (!name || !bookmarks[name]) {
|
|
2790
|
-
console.log(theme.error(`
|
|
5456
|
+
console.log(` ${miniSlime.sad} ${theme.error(`Bookmark not found: ${name}`)}`);
|
|
2791
5457
|
break;
|
|
2792
5458
|
}
|
|
2793
5459
|
delete bookmarks[name];
|
|
2794
5460
|
saveBookmarks(bookmarks);
|
|
2795
|
-
console.log(` ${
|
|
5461
|
+
console.log(` ${miniSlime.happy} ${theme.success("Deleted bookmark:")} ${theme.primary(name)}`);
|
|
2796
5462
|
break;
|
|
2797
5463
|
case "list":
|
|
2798
5464
|
case "ls":
|
|
@@ -2805,7 +5471,7 @@ function handleBookmark(args2) {
|
|
|
2805
5471
|
console.log(theme.primary(" \u{1F4CD} Directory Bookmarks"));
|
|
2806
5472
|
console.log("");
|
|
2807
5473
|
for (const key of keys.sort()) {
|
|
2808
|
-
const exists =
|
|
5474
|
+
const exists = existsSync11(bookmarks[key]);
|
|
2809
5475
|
const status = exists ? theme.success(symbols.check) : theme.error(symbols.cross);
|
|
2810
5476
|
console.log(` ${status} ${theme.primary(key.padEnd(15))} ${theme.dim("\u2192")} ${bookmarks[key]}`);
|
|
2811
5477
|
}
|
|
@@ -2822,10 +5488,10 @@ function handleFind(args2) {
|
|
|
2822
5488
|
function searchDir(dir, depth = 0) {
|
|
2823
5489
|
if (depth > 5 || results.length >= maxResults) return;
|
|
2824
5490
|
try {
|
|
2825
|
-
const entries =
|
|
5491
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
2826
5492
|
for (const entry of entries) {
|
|
2827
5493
|
if (entry.name.startsWith(".") || ["node_modules", ".git", "dist", "build"].includes(entry.name)) continue;
|
|
2828
|
-
const fullPath =
|
|
5494
|
+
const fullPath = resolve5(dir, entry.name);
|
|
2829
5495
|
const matchPattern = pattern.replace(/\*/g, ".*").replace(/\?/g, ".");
|
|
2830
5496
|
const regex = new RegExp(matchPattern, "i");
|
|
2831
5497
|
if (regex.test(entry.name)) {
|
|
@@ -2849,9 +5515,9 @@ function handleFind(args2) {
|
|
|
2849
5515
|
const relativePath = result.path.replace(searchPath, ".").replace(/\\/g, "/");
|
|
2850
5516
|
const ext = result.isDir ? "dir" : extname(result.path).toLowerCase();
|
|
2851
5517
|
const iconInfo = fileIcons[ext] || fileIcons["default"];
|
|
2852
|
-
const fileName =
|
|
5518
|
+
const fileName = basename4(result.path);
|
|
2853
5519
|
const dirPath = relativePath.slice(0, -fileName.length);
|
|
2854
|
-
console.log(` ${iconInfo.icon} ${theme.dim(dirPath)}${
|
|
5520
|
+
console.log(` ${iconInfo.icon} ${theme.dim(dirPath)}${chalk5.hex(iconInfo.color)(fileName)}${result.isDir ? "/" : ""}`);
|
|
2855
5521
|
}
|
|
2856
5522
|
if (results.length >= maxResults) {
|
|
2857
5523
|
console.log("");
|
|
@@ -2861,8 +5527,8 @@ function handleFind(args2) {
|
|
|
2861
5527
|
console.log("");
|
|
2862
5528
|
}
|
|
2863
5529
|
function handleDu(args2) {
|
|
2864
|
-
const targetPath = args2.trim() ?
|
|
2865
|
-
if (!
|
|
5530
|
+
const targetPath = args2.trim() ? resolve5(process.cwd(), args2.trim()) : process.cwd();
|
|
5531
|
+
if (!existsSync11(targetPath)) {
|
|
2866
5532
|
console.log(theme.error(` Path not found: ${targetPath}`));
|
|
2867
5533
|
return;
|
|
2868
5534
|
}
|
|
@@ -2870,10 +5536,10 @@ function handleDu(args2) {
|
|
|
2870
5536
|
console.log("");
|
|
2871
5537
|
console.log(theme.dim(" Calculating sizes..."));
|
|
2872
5538
|
try {
|
|
2873
|
-
const entries =
|
|
5539
|
+
const entries = readdirSync4(targetPath, { withFileTypes: true });
|
|
2874
5540
|
for (const entry of entries) {
|
|
2875
5541
|
if (entry.name.startsWith(".")) continue;
|
|
2876
|
-
const fullPath =
|
|
5542
|
+
const fullPath = resolve5(targetPath, entry.name);
|
|
2877
5543
|
let size = 0;
|
|
2878
5544
|
let skipped = false;
|
|
2879
5545
|
try {
|
|
@@ -2882,10 +5548,10 @@ function handleDu(args2) {
|
|
|
2882
5548
|
skipped = true;
|
|
2883
5549
|
size = 0;
|
|
2884
5550
|
} else {
|
|
2885
|
-
size =
|
|
5551
|
+
size = getDirSize2(fullPath);
|
|
2886
5552
|
}
|
|
2887
5553
|
} else {
|
|
2888
|
-
size =
|
|
5554
|
+
size = statSync2(fullPath).size;
|
|
2889
5555
|
}
|
|
2890
5556
|
items.push({ name: entry.name, size, isDir: entry.isDirectory(), skipped });
|
|
2891
5557
|
} catch {
|
|
@@ -2896,10 +5562,10 @@ function handleDu(args2) {
|
|
|
2896
5562
|
return;
|
|
2897
5563
|
}
|
|
2898
5564
|
process.stdout.write("\x1B[1A\x1B[2K");
|
|
2899
|
-
items.sort((a,
|
|
5565
|
+
items.sort((a, b3) => b3.size - a.size);
|
|
2900
5566
|
const totalSize = items.reduce((sum, item) => sum + item.size, 0);
|
|
2901
5567
|
console.log("");
|
|
2902
|
-
console.log(theme.primary(` \u{1F4CA} Disk Usage: ${
|
|
5568
|
+
console.log(theme.primary(` \u{1F4CA} Disk Usage: ${basename4(targetPath)}`));
|
|
2903
5569
|
console.log(theme.dim(` Total: ${formatSizeSimple(totalSize)}`));
|
|
2904
5570
|
console.log("");
|
|
2905
5571
|
const maxItems = 15;
|
|
@@ -2913,10 +5579,10 @@ function handleDu(args2) {
|
|
|
2913
5579
|
const percent = totalSize > 0 ? item.size / totalSize * 100 : 0;
|
|
2914
5580
|
const barWidth = 20;
|
|
2915
5581
|
const filled = Math.round(percent / 100 * barWidth);
|
|
2916
|
-
const bar =
|
|
5582
|
+
const bar = chalk5.hex("#4ECDC4")("\u2588".repeat(filled)) + chalk5.dim("\u2591".repeat(barWidth - filled));
|
|
2917
5583
|
const icon = item.isDir ? "\u{1F4C1}" : "\u{1F4C4}";
|
|
2918
5584
|
const name = item.name.length > 25 ? item.name.slice(0, 22) + "..." : item.name.padEnd(25);
|
|
2919
|
-
console.log(` ${icon} ${name} ${bar} ${formatSizeSimple(item.size).padStart(8)} ${
|
|
5585
|
+
console.log(` ${icon} ${name} ${bar} ${formatSizeSimple(item.size).padStart(8)} ${chalk5.dim(`${percent.toFixed(1)}%`)}`);
|
|
2920
5586
|
}
|
|
2921
5587
|
if (items.length > maxItems) {
|
|
2922
5588
|
console.log(theme.dim(` ... and ${items.length - maxItems} more items`));
|
|
@@ -2928,19 +5594,19 @@ function handleDu(args2) {
|
|
|
2928
5594
|
console.log("");
|
|
2929
5595
|
}
|
|
2930
5596
|
var skipDirs = /* @__PURE__ */ new Set(["node_modules", ".git", "dist", "build", ".next", ".nuxt", "coverage", ".cache", "__pycache__", "venv", ".venv"]);
|
|
2931
|
-
function
|
|
5597
|
+
function getDirSize2(dir, depth = 0, maxDepth = 4) {
|
|
2932
5598
|
if (depth > maxDepth) return 0;
|
|
2933
5599
|
let size = 0;
|
|
2934
5600
|
try {
|
|
2935
|
-
const entries =
|
|
5601
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
2936
5602
|
for (const entry of entries) {
|
|
2937
5603
|
if (skipDirs.has(entry.name)) continue;
|
|
2938
|
-
const fullPath =
|
|
5604
|
+
const fullPath = resolve5(dir, entry.name);
|
|
2939
5605
|
if (entry.isDirectory()) {
|
|
2940
|
-
size +=
|
|
5606
|
+
size += getDirSize2(fullPath, depth + 1, maxDepth);
|
|
2941
5607
|
} else {
|
|
2942
5608
|
try {
|
|
2943
|
-
size +=
|
|
5609
|
+
size += statSync2(fullPath).size;
|
|
2944
5610
|
} catch {
|
|
2945
5611
|
}
|
|
2946
5612
|
}
|
|
@@ -2959,7 +5625,7 @@ function handleGit(args2) {
|
|
|
2959
5625
|
const subcommand = args2.trim().split(/\s+/)[0] || "status";
|
|
2960
5626
|
console.log("");
|
|
2961
5627
|
try {
|
|
2962
|
-
|
|
5628
|
+
execSync3("git rev-parse --is-inside-work-tree", { stdio: "pipe", timeout: 5e3 });
|
|
2963
5629
|
} catch {
|
|
2964
5630
|
console.log(theme.error(" Not a git repository"));
|
|
2965
5631
|
console.log("");
|
|
@@ -2969,10 +5635,10 @@ function handleGit(args2) {
|
|
|
2969
5635
|
switch (subcommand) {
|
|
2970
5636
|
case "status":
|
|
2971
5637
|
case "s": {
|
|
2972
|
-
const branch =
|
|
2973
|
-
console.log(` ${symbols.rocket} ${theme.primary("Branch:")} ${
|
|
5638
|
+
const branch = execSync3("git branch --show-current", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
5639
|
+
console.log(` ${symbols.rocket} ${theme.primary("Branch:")} ${chalk5.hex("#98C379")(branch)}`);
|
|
2974
5640
|
console.log("");
|
|
2975
|
-
const status =
|
|
5641
|
+
const status = execSync3("git status --porcelain", { encoding: "utf-8", timeout: 5e3 });
|
|
2976
5642
|
if (!status.trim()) {
|
|
2977
5643
|
console.log(` ${symbols.check} ${theme.success("Working tree clean")}`);
|
|
2978
5644
|
} else {
|
|
@@ -2995,12 +5661,12 @@ function handleGit(args2) {
|
|
|
2995
5661
|
}
|
|
2996
5662
|
if (staged.length > 0) {
|
|
2997
5663
|
console.log(theme.success(" Staged changes:"));
|
|
2998
|
-
staged.forEach((f) => console.log(` ${symbols.check} ${
|
|
5664
|
+
staged.forEach((f) => console.log(` ${symbols.check} ${chalk5.hex("#98C379")(f)}`));
|
|
2999
5665
|
console.log("");
|
|
3000
5666
|
}
|
|
3001
5667
|
if (modified.length > 0) {
|
|
3002
5668
|
console.log(theme.warning(" Modified:"));
|
|
3003
|
-
modified.forEach((f) => console.log(` ${symbols.bullet} ${
|
|
5669
|
+
modified.forEach((f) => console.log(` ${symbols.bullet} ${chalk5.hex("#E5C07B")(f)}`));
|
|
3004
5670
|
console.log("");
|
|
3005
5671
|
}
|
|
3006
5672
|
if (untracked.length > 0) {
|
|
@@ -3009,35 +5675,35 @@ function handleGit(args2) {
|
|
|
3009
5675
|
console.log("");
|
|
3010
5676
|
}
|
|
3011
5677
|
}
|
|
3012
|
-
const log =
|
|
5678
|
+
const log = execSync3("git log --oneline -3", { encoding: "utf-8", timeout: 3e3 }).trim();
|
|
3013
5679
|
if (log) {
|
|
3014
5680
|
console.log(theme.dim(" Recent commits:"));
|
|
3015
5681
|
log.split("\n").forEach((line) => {
|
|
3016
5682
|
const [hash, ...msg] = line.split(" ");
|
|
3017
|
-
console.log(` ${
|
|
5683
|
+
console.log(` ${chalk5.hex("#61AFEF")(hash)} ${theme.dim(msg.join(" "))}`);
|
|
3018
5684
|
});
|
|
3019
5685
|
}
|
|
3020
5686
|
break;
|
|
3021
5687
|
}
|
|
3022
5688
|
case "log":
|
|
3023
5689
|
case "l": {
|
|
3024
|
-
const log =
|
|
5690
|
+
const log = execSync3("git log --oneline -10", { encoding: "utf-8", timeout: 3e3 }).trim();
|
|
3025
5691
|
console.log(theme.primary(" \u{1F4DC} Recent Commits"));
|
|
3026
5692
|
console.log("");
|
|
3027
5693
|
log.split("\n").forEach((line) => {
|
|
3028
5694
|
const [hash, ...msg] = line.split(" ");
|
|
3029
|
-
console.log(` ${
|
|
5695
|
+
console.log(` ${chalk5.hex("#61AFEF")(hash)} ${msg.join(" ")}`);
|
|
3030
5696
|
});
|
|
3031
5697
|
break;
|
|
3032
5698
|
}
|
|
3033
5699
|
case "branch":
|
|
3034
5700
|
case "b": {
|
|
3035
|
-
const branches =
|
|
5701
|
+
const branches = execSync3("git branch -a", { encoding: "utf-8", timeout: 3e3 }).trim();
|
|
3036
5702
|
console.log(theme.primary(" \u{1F33F} Branches"));
|
|
3037
5703
|
console.log("");
|
|
3038
5704
|
branches.split("\n").forEach((line) => {
|
|
3039
5705
|
if (line.startsWith("*")) {
|
|
3040
|
-
console.log(` ${
|
|
5706
|
+
console.log(` ${chalk5.hex("#98C379")(line)}`);
|
|
3041
5707
|
} else {
|
|
3042
5708
|
console.log(` ${theme.dim(line)}`);
|
|
3043
5709
|
}
|
|
@@ -3045,7 +5711,7 @@ function handleGit(args2) {
|
|
|
3045
5711
|
break;
|
|
3046
5712
|
}
|
|
3047
5713
|
default:
|
|
3048
|
-
const result =
|
|
5714
|
+
const result = execSync3(`git ${args2}`, { encoding: "utf-8", timeout: 1e4 });
|
|
3049
5715
|
console.log(result);
|
|
3050
5716
|
}
|
|
3051
5717
|
} catch (error) {
|
|
@@ -3054,23 +5720,23 @@ function handleGit(args2) {
|
|
|
3054
5720
|
}
|
|
3055
5721
|
console.log("");
|
|
3056
5722
|
}
|
|
3057
|
-
var isMac =
|
|
3058
|
-
var isLinux =
|
|
5723
|
+
var isMac = platform3() === "darwin";
|
|
5724
|
+
var isLinux = platform3() === "linux";
|
|
3059
5725
|
function getClipboardCopyCmd() {
|
|
3060
|
-
if (
|
|
5726
|
+
if (isWindows2) return "clip";
|
|
3061
5727
|
if (isMac) return "pbcopy";
|
|
3062
5728
|
try {
|
|
3063
|
-
|
|
5729
|
+
execSync3("which xclip", { stdio: "pipe" });
|
|
3064
5730
|
return "xclip -selection clipboard";
|
|
3065
5731
|
} catch {
|
|
3066
5732
|
return "xsel --clipboard --input";
|
|
3067
5733
|
}
|
|
3068
5734
|
}
|
|
3069
5735
|
function getClipboardPasteCmd() {
|
|
3070
|
-
if (
|
|
5736
|
+
if (isWindows2) return 'powershell -command "Get-Clipboard"';
|
|
3071
5737
|
if (isMac) return "pbpaste";
|
|
3072
5738
|
try {
|
|
3073
|
-
|
|
5739
|
+
execSync3("which xclip", { stdio: "pipe" });
|
|
3074
5740
|
return "xclip -selection clipboard -o";
|
|
3075
5741
|
} catch {
|
|
3076
5742
|
return "xsel --clipboard --output";
|
|
@@ -3084,10 +5750,10 @@ function handleClipboard(args2) {
|
|
|
3084
5750
|
if (action === "copy" && content) {
|
|
3085
5751
|
try {
|
|
3086
5752
|
const copyCmd = getClipboardCopyCmd();
|
|
3087
|
-
if (
|
|
3088
|
-
|
|
5753
|
+
if (isWindows2) {
|
|
5754
|
+
execSync3(`echo ${content} | ${copyCmd}`, { stdio: "pipe", timeout: 3e3 });
|
|
3089
5755
|
} else {
|
|
3090
|
-
|
|
5756
|
+
execSync3(`echo "${content}" | ${copyCmd}`, { stdio: "pipe", timeout: 3e3 });
|
|
3091
5757
|
}
|
|
3092
5758
|
console.log(` ${symbols.check} ${theme.success("Copied to clipboard")}`);
|
|
3093
5759
|
} catch {
|
|
@@ -3099,7 +5765,7 @@ function handleClipboard(args2) {
|
|
|
3099
5765
|
} else if (action === "paste") {
|
|
3100
5766
|
try {
|
|
3101
5767
|
const pasteCmd = getClipboardPasteCmd();
|
|
3102
|
-
const result =
|
|
5768
|
+
const result = execSync3(pasteCmd, { encoding: "utf-8", timeout: 3e3 }).trim();
|
|
3103
5769
|
console.log(` ${symbols.clipboard} ${theme.dim("Clipboard contents:")}`);
|
|
3104
5770
|
console.log("");
|
|
3105
5771
|
console.log(result);
|
|
@@ -3110,14 +5776,14 @@ function handleClipboard(args2) {
|
|
|
3110
5776
|
}
|
|
3111
5777
|
}
|
|
3112
5778
|
} else if (action === "file" && parts[1]) {
|
|
3113
|
-
const filePath =
|
|
3114
|
-
if (
|
|
5779
|
+
const filePath = resolve5(process.cwd(), parts[1]);
|
|
5780
|
+
if (existsSync11(filePath)) {
|
|
3115
5781
|
try {
|
|
3116
5782
|
const copyCmd = getClipboardCopyCmd();
|
|
3117
|
-
if (
|
|
3118
|
-
|
|
5783
|
+
if (isWindows2) {
|
|
5784
|
+
execSync3(`type "${filePath}" | ${copyCmd}`, { stdio: "pipe", timeout: 5e3 });
|
|
3119
5785
|
} else {
|
|
3120
|
-
|
|
5786
|
+
execSync3(`cat "${filePath}" | ${copyCmd}`, { stdio: "pipe", timeout: 5e3 });
|
|
3121
5787
|
}
|
|
3122
5788
|
console.log(` ${symbols.check} ${theme.success("File contents copied to clipboard")}`);
|
|
3123
5789
|
} catch {
|
|
@@ -3143,14 +5809,14 @@ function handleClipboard(args2) {
|
|
|
3143
5809
|
console.log("");
|
|
3144
5810
|
}
|
|
3145
5811
|
function handlePretty(args2) {
|
|
3146
|
-
const filePath =
|
|
3147
|
-
if (!
|
|
5812
|
+
const filePath = resolve5(process.cwd(), args2.trim());
|
|
5813
|
+
if (!existsSync11(filePath)) {
|
|
3148
5814
|
console.log(theme.error(` File not found: ${args2}`));
|
|
3149
5815
|
return;
|
|
3150
5816
|
}
|
|
3151
5817
|
console.log("");
|
|
3152
5818
|
try {
|
|
3153
|
-
const content =
|
|
5819
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
3154
5820
|
const ext = extname(filePath).toLowerCase();
|
|
3155
5821
|
if (ext === ".json") {
|
|
3156
5822
|
const parsed = JSON.parse(content);
|
|
@@ -3166,7 +5832,7 @@ function handlePretty(args2) {
|
|
|
3166
5832
|
console.log("");
|
|
3167
5833
|
}
|
|
3168
5834
|
function highlightJson(json) {
|
|
3169
|
-
return json.replace(/"([^"]+)":/g, (_, key) =>
|
|
5835
|
+
return json.replace(/"([^"]+)":/g, (_, key) => chalk5.hex("#E06C75")(`"${key}"`) + ":").replace(/: "([^"]*)"/g, (_, val) => ": " + chalk5.hex("#98C379")(`"${val}"`)).replace(/: (\d+)/g, (_, num) => ": " + chalk5.hex("#D19A66")(num)).replace(/: (true|false)/g, (_, bool) => ": " + chalk5.hex("#56B6C2")(bool)).replace(/: (null)/g, (_, n) => ": " + chalk5.hex("#C678DD")(n));
|
|
3170
5836
|
}
|
|
3171
5837
|
var activeWatcher = null;
|
|
3172
5838
|
function handleWatch(args2) {
|
|
@@ -3197,14 +5863,14 @@ function handleWatch(args2) {
|
|
|
3197
5863
|
console.log("");
|
|
3198
5864
|
return;
|
|
3199
5865
|
}
|
|
3200
|
-
const filePath =
|
|
3201
|
-
if (!
|
|
5866
|
+
const filePath = resolve5(process.cwd(), action);
|
|
5867
|
+
if (!existsSync11(filePath)) {
|
|
3202
5868
|
console.log(theme.error(` File not found: ${action}`));
|
|
3203
5869
|
console.log("");
|
|
3204
5870
|
return;
|
|
3205
5871
|
}
|
|
3206
5872
|
try {
|
|
3207
|
-
const stats =
|
|
5873
|
+
const stats = statSync2(filePath);
|
|
3208
5874
|
if (stats.isDirectory()) {
|
|
3209
5875
|
console.log(theme.error(` Cannot watch a directory: ${action}`));
|
|
3210
5876
|
console.log(theme.dim(" Please specify a file path"));
|
|
@@ -3220,12 +5886,12 @@ function handleWatch(args2) {
|
|
|
3220
5886
|
unwatchFile(activeWatcher);
|
|
3221
5887
|
}
|
|
3222
5888
|
activeWatcher = filePath;
|
|
3223
|
-
let lastSize =
|
|
5889
|
+
let lastSize = statSync2(filePath).size;
|
|
3224
5890
|
console.log(` ${symbols.info} ${theme.primary("Watching:")} ${filePath}`);
|
|
3225
5891
|
console.log(theme.dim(' (Type "!watch stop" to stop watching)'));
|
|
3226
5892
|
console.log("");
|
|
3227
5893
|
try {
|
|
3228
|
-
const content =
|
|
5894
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
3229
5895
|
const lines = content.split("\n").slice(-10);
|
|
3230
5896
|
lines.forEach((line) => console.log(theme.dim(line)));
|
|
3231
5897
|
} catch {
|
|
@@ -3234,12 +5900,12 @@ function handleWatch(args2) {
|
|
|
3234
5900
|
watchFile(filePath, { interval: 500 }, (curr, prev) => {
|
|
3235
5901
|
if (curr.size > lastSize) {
|
|
3236
5902
|
try {
|
|
3237
|
-
const newContent =
|
|
5903
|
+
const newContent = readFileSync9(filePath, "utf-8");
|
|
3238
5904
|
const allLines = newContent.split("\n");
|
|
3239
5905
|
const oldLines = Math.floor(prev.size / 50);
|
|
3240
5906
|
const newLines = allLines.slice(-Math.max(1, allLines.length - oldLines));
|
|
3241
5907
|
newLines.forEach((line) => {
|
|
3242
|
-
if (line.trim()) console.log(
|
|
5908
|
+
if (line.trim()) console.log(chalk5.hex("#98C379")(line));
|
|
3243
5909
|
});
|
|
3244
5910
|
} catch {
|
|
3245
5911
|
}
|
|
@@ -3252,12 +5918,12 @@ function handleServe(args2) {
|
|
|
3252
5918
|
console.log("");
|
|
3253
5919
|
console.log(` ${symbols.rocket} ${theme.primary("Starting HTTP server...")}`);
|
|
3254
5920
|
console.log(` ${theme.dim("Serving:")} ${process.cwd()}`);
|
|
3255
|
-
console.log(` ${theme.dim("URL:")} ${
|
|
5921
|
+
console.log(` ${theme.dim("URL:")} ${chalk5.hex("#61AFEF")(`http://localhost:${port}`)}`);
|
|
3256
5922
|
console.log("");
|
|
3257
5923
|
console.log(theme.dim(" Press Ctrl+C to stop"));
|
|
3258
5924
|
console.log("");
|
|
3259
5925
|
try {
|
|
3260
|
-
const npx =
|
|
5926
|
+
const npx = isWindows2 ? "npx.cmd" : "npx";
|
|
3261
5927
|
spawn(npx, ["serve", "-p", String(port)], {
|
|
3262
5928
|
cwd: process.cwd(),
|
|
3263
5929
|
stdio: "inherit"
|
|
@@ -3272,8 +5938,8 @@ function handlePs() {
|
|
|
3272
5938
|
console.log("");
|
|
3273
5939
|
try {
|
|
3274
5940
|
let result;
|
|
3275
|
-
if (
|
|
3276
|
-
result =
|
|
5941
|
+
if (isWindows2) {
|
|
5942
|
+
result = execSync3('tasklist /FO CSV /NH | findstr /V "System Idle"', { encoding: "utf-8", timeout: 5e3 });
|
|
3277
5943
|
const lines = result.trim().split("\n").slice(0, 15);
|
|
3278
5944
|
console.log(theme.dim(" Name PID Memory"));
|
|
3279
5945
|
console.log(theme.dim(" \u2500".repeat(25)));
|
|
@@ -3283,11 +5949,11 @@ function handlePs() {
|
|
|
3283
5949
|
const name = parts[0].slice(0, 28).padEnd(30);
|
|
3284
5950
|
const pid = parts[1].padStart(8);
|
|
3285
5951
|
const mem = parts[4];
|
|
3286
|
-
console.log(` ${name}${pid} ${
|
|
5952
|
+
console.log(` ${name}${pid} ${chalk5.hex("#98C379")(mem)}`);
|
|
3287
5953
|
}
|
|
3288
5954
|
});
|
|
3289
5955
|
} else {
|
|
3290
|
-
result =
|
|
5956
|
+
result = execSync3("ps aux | head -15", { encoding: "utf-8", timeout: 5e3 });
|
|
3291
5957
|
console.log(result);
|
|
3292
5958
|
}
|
|
3293
5959
|
} catch {
|
|
@@ -3310,7 +5976,7 @@ function handleEnv(args2) {
|
|
|
3310
5976
|
filtered.slice(0, maxShow).forEach((key) => {
|
|
3311
5977
|
const value = env[key] || "";
|
|
3312
5978
|
const displayValue = value.length > 50 ? value.slice(0, 47) + "..." : value;
|
|
3313
|
-
console.log(` ${
|
|
5979
|
+
console.log(` ${chalk5.hex("#E06C75")(key.padEnd(25))} ${theme.dim("=")} ${displayValue}`);
|
|
3314
5980
|
});
|
|
3315
5981
|
if (filtered.length > maxShow) {
|
|
3316
5982
|
console.log(theme.dim(` ... and ${filtered.length - maxShow} more`));
|
|
@@ -3339,22 +6005,22 @@ function handleIp() {
|
|
|
3339
6005
|
if (localIps.length === 0) {
|
|
3340
6006
|
console.log(` ${theme.dim("(No network interfaces found)")}`);
|
|
3341
6007
|
} else {
|
|
3342
|
-
localIps.forEach((ip) => console.log(` ${
|
|
6008
|
+
localIps.forEach((ip) => console.log(` ${chalk5.hex("#98C379")(ip)}`));
|
|
3343
6009
|
}
|
|
3344
6010
|
console.log("");
|
|
3345
6011
|
console.log(theme.dim(" Public IP:"));
|
|
3346
6012
|
try {
|
|
3347
6013
|
let result;
|
|
3348
6014
|
try {
|
|
3349
|
-
result =
|
|
6015
|
+
result = execSync3("curl -s ifconfig.me", { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
3350
6016
|
} catch {
|
|
3351
|
-
if (
|
|
3352
|
-
result =
|
|
6017
|
+
if (isWindows2) {
|
|
6018
|
+
result = execSync3('powershell -command "(Invoke-WebRequest -Uri ifconfig.me -UseBasicParsing).Content"', { encoding: "utf-8", timeout: 5e3 }).trim();
|
|
3353
6019
|
} else {
|
|
3354
6020
|
throw new Error("curl not available");
|
|
3355
6021
|
}
|
|
3356
6022
|
}
|
|
3357
|
-
console.log(` ${
|
|
6023
|
+
console.log(` ${chalk5.hex("#61AFEF")(result)}`);
|
|
3358
6024
|
} catch {
|
|
3359
6025
|
console.log(` ${theme.dim("(Could not fetch - requires curl or internet)")}`);
|
|
3360
6026
|
}
|
|
@@ -3371,21 +6037,21 @@ function handleEpoch(args2) {
|
|
|
3371
6037
|
if (!input || input === "now") {
|
|
3372
6038
|
const now = /* @__PURE__ */ new Date();
|
|
3373
6039
|
console.log(` ${theme.dim("Current Time:")}`);
|
|
3374
|
-
console.log(` ${
|
|
3375
|
-
console.log(` ${
|
|
3376
|
-
console.log(` ${
|
|
6040
|
+
console.log(` ${chalk5.hex("#98C379")(now.toISOString())}`);
|
|
6041
|
+
console.log(` ${chalk5.hex("#61AFEF")(Math.floor(now.getTime() / 1e3).toString())} ${theme.dim("(Unix seconds)")}`);
|
|
6042
|
+
console.log(` ${chalk5.hex("#E5C07B")(now.getTime().toString())} ${theme.dim("(Unix milliseconds)")}`);
|
|
3377
6043
|
} else if (/^\d{10,13}$/.test(input)) {
|
|
3378
6044
|
const ms = input.length === 10 ? parseInt(input) * 1e3 : parseInt(input);
|
|
3379
6045
|
const date = new Date(ms);
|
|
3380
|
-
console.log(` ${theme.dim("Epoch:")} ${
|
|
3381
|
-
console.log(` ${theme.dim("Date:")} ${
|
|
3382
|
-
console.log(` ${
|
|
6046
|
+
console.log(` ${theme.dim("Epoch:")} ${chalk5.hex("#E5C07B")(input)}`);
|
|
6047
|
+
console.log(` ${theme.dim("Date:")} ${chalk5.hex("#98C379")(date.toISOString())}`);
|
|
6048
|
+
console.log(` ${chalk5.hex("#98C379")(date.toLocaleString())}`);
|
|
3383
6049
|
} else {
|
|
3384
6050
|
const date = new Date(input);
|
|
3385
6051
|
if (!isNaN(date.getTime())) {
|
|
3386
|
-
console.log(` ${theme.dim("Date:")} ${
|
|
3387
|
-
console.log(` ${theme.dim("Unix:")} ${
|
|
3388
|
-
console.log(` ${
|
|
6052
|
+
console.log(` ${theme.dim("Date:")} ${chalk5.hex("#98C379")(input)}`);
|
|
6053
|
+
console.log(` ${theme.dim("Unix:")} ${chalk5.hex("#61AFEF")(Math.floor(date.getTime() / 1e3).toString())} ${theme.dim("(seconds)")}`);
|
|
6054
|
+
console.log(` ${chalk5.hex("#E5C07B")(date.getTime().toString())} ${theme.dim("(milliseconds)")}`);
|
|
3389
6055
|
} else {
|
|
3390
6056
|
console.log(theme.error(` Cannot parse: ${input}`));
|
|
3391
6057
|
console.log(theme.dim(' Examples: epoch now, epoch 1703788800, epoch "2024-01-01"'));
|
|
@@ -3409,19 +6075,19 @@ async function handleHttp(args2) {
|
|
|
3409
6075
|
return;
|
|
3410
6076
|
}
|
|
3411
6077
|
const fullUrl = url.startsWith("http") ? url : `https://${url}`;
|
|
3412
|
-
console.log(` ${symbols.rocket} ${theme.dim(method)} ${
|
|
6078
|
+
console.log(` ${symbols.rocket} ${theme.dim(method)} ${chalk5.hex("#61AFEF")(fullUrl)}`);
|
|
3413
6079
|
console.log("");
|
|
3414
6080
|
try {
|
|
3415
6081
|
let result;
|
|
3416
6082
|
try {
|
|
3417
6083
|
const curlCmd = method === "HEAD" ? `curl -sI "${fullUrl}"` : `curl -s -X ${method} "${fullUrl}"`;
|
|
3418
|
-
result =
|
|
6084
|
+
result = execSync3(curlCmd, { encoding: "utf-8", timeout: 1e4 });
|
|
3419
6085
|
} catch {
|
|
3420
|
-
if (
|
|
6086
|
+
if (isWindows2) {
|
|
3421
6087
|
if (method === "HEAD") {
|
|
3422
|
-
result =
|
|
6088
|
+
result = execSync3(`powershell -command "(Invoke-WebRequest -Uri '${fullUrl}' -Method Head -UseBasicParsing).Headers | ConvertTo-Json"`, { encoding: "utf-8", timeout: 1e4 });
|
|
3423
6089
|
} else {
|
|
3424
|
-
result =
|
|
6090
|
+
result = execSync3(`powershell -command "(Invoke-WebRequest -Uri '${fullUrl}' -Method ${method} -UseBasicParsing).Content"`, { encoding: "utf-8", timeout: 1e4 });
|
|
3425
6091
|
}
|
|
3426
6092
|
} else {
|
|
3427
6093
|
throw new Error("curl not available");
|
|
@@ -3459,22 +6125,22 @@ function handleDiff(args2) {
|
|
|
3459
6125
|
console.log("");
|
|
3460
6126
|
return;
|
|
3461
6127
|
}
|
|
3462
|
-
const path1 =
|
|
3463
|
-
const path2 =
|
|
3464
|
-
if (!
|
|
6128
|
+
const path1 = resolve5(process.cwd(), file1);
|
|
6129
|
+
const path2 = resolve5(process.cwd(), file2);
|
|
6130
|
+
if (!existsSync11(path1)) {
|
|
3465
6131
|
console.log(theme.error(` File not found: ${file1}`));
|
|
3466
6132
|
console.log("");
|
|
3467
6133
|
return;
|
|
3468
6134
|
}
|
|
3469
|
-
if (!
|
|
6135
|
+
if (!existsSync11(path2)) {
|
|
3470
6136
|
console.log(theme.error(` File not found: ${file2}`));
|
|
3471
6137
|
console.log("");
|
|
3472
6138
|
return;
|
|
3473
6139
|
}
|
|
3474
6140
|
try {
|
|
3475
|
-
const content1 =
|
|
3476
|
-
const content2 =
|
|
3477
|
-
console.log(theme.primary(` Comparing: ${
|
|
6141
|
+
const content1 = readFileSync9(path1, "utf-8").split("\n");
|
|
6142
|
+
const content2 = readFileSync9(path2, "utf-8").split("\n");
|
|
6143
|
+
console.log(theme.primary(` Comparing: ${basename4(file1)} \u2194 ${basename4(file2)}`));
|
|
3478
6144
|
console.log("");
|
|
3479
6145
|
const maxLines = Math.max(content1.length, content2.length);
|
|
3480
6146
|
let differences = 0;
|
|
@@ -3483,8 +6149,8 @@ function handleDiff(args2) {
|
|
|
3483
6149
|
const line2 = content2[i] || "";
|
|
3484
6150
|
if (line1 !== line2) {
|
|
3485
6151
|
differences++;
|
|
3486
|
-
console.log(
|
|
3487
|
-
console.log(
|
|
6152
|
+
console.log(chalk5.hex("#E06C75")(` - ${(i + 1).toString().padStart(4)}: ${line1.slice(0, 70)}`));
|
|
6153
|
+
console.log(chalk5.hex("#98C379")(` + ${(i + 1).toString().padStart(4)}: ${line2.slice(0, 70)}`));
|
|
3488
6154
|
console.log("");
|
|
3489
6155
|
}
|
|
3490
6156
|
}
|
|
@@ -3501,18 +6167,18 @@ function handleDiff(args2) {
|
|
|
3501
6167
|
}
|
|
3502
6168
|
console.log("");
|
|
3503
6169
|
}
|
|
3504
|
-
var aliasesFile =
|
|
6170
|
+
var aliasesFile = join9(homedir5(), ".zammy-aliases.json");
|
|
3505
6171
|
function loadAliases() {
|
|
3506
6172
|
try {
|
|
3507
|
-
if (
|
|
3508
|
-
return JSON.parse(
|
|
6173
|
+
if (existsSync11(aliasesFile)) {
|
|
6174
|
+
return JSON.parse(readFileSync9(aliasesFile, "utf-8"));
|
|
3509
6175
|
}
|
|
3510
6176
|
} catch {
|
|
3511
6177
|
}
|
|
3512
6178
|
return {};
|
|
3513
6179
|
}
|
|
3514
6180
|
function saveAliases(aliases) {
|
|
3515
|
-
|
|
6181
|
+
writeFileSync6(aliasesFile, JSON.stringify(aliases, null, 2));
|
|
3516
6182
|
}
|
|
3517
6183
|
function handleAlias(args2) {
|
|
3518
6184
|
const parts = args2.trim().split(/\s+/);
|
|
@@ -3542,7 +6208,7 @@ function handleAlias(args2) {
|
|
|
3542
6208
|
console.log(theme.dim(` Running: ${aliases[name]}`));
|
|
3543
6209
|
console.log("");
|
|
3544
6210
|
try {
|
|
3545
|
-
const result =
|
|
6211
|
+
const result = execSync3(aliases[name], { encoding: "utf-8", cwd: process.cwd(), timeout: 3e4 });
|
|
3546
6212
|
console.log(result);
|
|
3547
6213
|
} catch (error) {
|
|
3548
6214
|
const err = error;
|
|
@@ -3574,7 +6240,7 @@ function handleNotify(args2) {
|
|
|
3574
6240
|
const message = args2.trim() || "Notification from Zammy CLI";
|
|
3575
6241
|
console.log("");
|
|
3576
6242
|
try {
|
|
3577
|
-
if (
|
|
6243
|
+
if (isWindows2) {
|
|
3578
6244
|
const ps = `
|
|
3579
6245
|
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
|
|
3580
6246
|
[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null
|
|
@@ -3583,10 +6249,10 @@ function handleNotify(args2) {
|
|
|
3583
6249
|
$notifier = [Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Zammy CLI")
|
|
3584
6250
|
$notifier.Show([Windows.UI.Notifications.ToastNotification]::new($template))
|
|
3585
6251
|
`;
|
|
3586
|
-
|
|
6252
|
+
execSync3(`powershell -command "${ps.replace(/\n/g, " ")}"`, { stdio: "pipe", timeout: 5e3 });
|
|
3587
6253
|
} else {
|
|
3588
|
-
const cmd =
|
|
3589
|
-
|
|
6254
|
+
const cmd = platform3() === "darwin" ? `osascript -e 'display notification "${message}" with title "Zammy CLI"'` : `notify-send "Zammy CLI" "${message}"`;
|
|
6255
|
+
execSync3(cmd, { stdio: "pipe", timeout: 3e3 });
|
|
3590
6256
|
}
|
|
3591
6257
|
console.log(` ${symbols.check} ${theme.success("Notification sent")}`);
|
|
3592
6258
|
} catch {
|
|
@@ -3616,7 +6282,7 @@ function handleGrep(args2) {
|
|
|
3616
6282
|
function searchFile(filePath) {
|
|
3617
6283
|
if (results.length >= maxResults) return;
|
|
3618
6284
|
try {
|
|
3619
|
-
const content =
|
|
6285
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
3620
6286
|
const lines = content.split("\n");
|
|
3621
6287
|
lines.forEach((line, index) => {
|
|
3622
6288
|
if (results.length >= maxResults) return;
|
|
@@ -3634,11 +6300,11 @@ function handleGrep(args2) {
|
|
|
3634
6300
|
function searchDir(dir, depth = 0) {
|
|
3635
6301
|
if (depth > 4 || results.length >= maxResults) return;
|
|
3636
6302
|
try {
|
|
3637
|
-
const entries =
|
|
6303
|
+
const entries = readdirSync4(dir, { withFileTypes: true });
|
|
3638
6304
|
for (const entry of entries) {
|
|
3639
6305
|
if (results.length >= maxResults) break;
|
|
3640
6306
|
if (entry.name.startsWith(".") || ["node_modules", ".git", "dist", "build"].includes(entry.name)) continue;
|
|
3641
|
-
const fullPath =
|
|
6307
|
+
const fullPath = resolve5(dir, entry.name);
|
|
3642
6308
|
if (entry.isDirectory()) {
|
|
3643
6309
|
searchDir(fullPath, depth + 1);
|
|
3644
6310
|
} else {
|
|
@@ -3661,9 +6327,9 @@ function handleGrep(args2) {
|
|
|
3661
6327
|
for (const result of results) {
|
|
3662
6328
|
const highlightedContent = result.content.replace(
|
|
3663
6329
|
regex,
|
|
3664
|
-
(match) =>
|
|
6330
|
+
(match) => chalk5.hex("#FF6B6B").bold(match)
|
|
3665
6331
|
);
|
|
3666
|
-
console.log(` ${theme.dim(result.file)}:${
|
|
6332
|
+
console.log(` ${theme.dim(result.file)}:${chalk5.hex("#61AFEF")(result.line)}`);
|
|
3667
6333
|
console.log(` ${highlightedContent}`);
|
|
3668
6334
|
console.log("");
|
|
3669
6335
|
}
|
|
@@ -3674,30 +6340,30 @@ function handleGrep(args2) {
|
|
|
3674
6340
|
console.log("");
|
|
3675
6341
|
}
|
|
3676
6342
|
function handleWc(args2) {
|
|
3677
|
-
const filePath =
|
|
6343
|
+
const filePath = resolve5(process.cwd(), args2.trim());
|
|
3678
6344
|
console.log("");
|
|
3679
6345
|
if (!args2.trim()) {
|
|
3680
6346
|
console.log(theme.error(" Usage: wc <file>"));
|
|
3681
6347
|
console.log("");
|
|
3682
6348
|
return;
|
|
3683
6349
|
}
|
|
3684
|
-
if (!
|
|
6350
|
+
if (!existsSync11(filePath)) {
|
|
3685
6351
|
console.log(theme.error(` File not found: ${args2}`));
|
|
3686
6352
|
console.log("");
|
|
3687
6353
|
return;
|
|
3688
6354
|
}
|
|
3689
6355
|
try {
|
|
3690
|
-
const content =
|
|
6356
|
+
const content = readFileSync9(filePath, "utf-8");
|
|
3691
6357
|
const lines = content.split("\n").length;
|
|
3692
6358
|
const words = content.split(/\s+/).filter((w) => w.length > 0).length;
|
|
3693
6359
|
const chars = content.length;
|
|
3694
6360
|
const bytes = Buffer.byteLength(content, "utf-8");
|
|
3695
|
-
console.log(theme.primary(` \u{1F4CA} ${
|
|
6361
|
+
console.log(theme.primary(` \u{1F4CA} ${basename4(args2)}`));
|
|
3696
6362
|
console.log("");
|
|
3697
|
-
console.log(` ${
|
|
3698
|
-
console.log(` ${
|
|
3699
|
-
console.log(` ${
|
|
3700
|
-
console.log(` ${
|
|
6363
|
+
console.log(` ${chalk5.hex("#61AFEF")(lines.toLocaleString().padStart(8))} ${theme.dim("lines")}`);
|
|
6364
|
+
console.log(` ${chalk5.hex("#98C379")(words.toLocaleString().padStart(8))} ${theme.dim("words")}`);
|
|
6365
|
+
console.log(` ${chalk5.hex("#E5C07B")(chars.toLocaleString().padStart(8))} ${theme.dim("characters")}`);
|
|
6366
|
+
console.log(` ${chalk5.hex("#C678DD")(bytes.toLocaleString().padStart(8))} ${theme.dim("bytes")}`);
|
|
3701
6367
|
} catch {
|
|
3702
6368
|
console.log(theme.error(" Failed to read file"));
|
|
3703
6369
|
}
|
|
@@ -3717,19 +6383,19 @@ function handleHead(args2) {
|
|
|
3717
6383
|
console.log("");
|
|
3718
6384
|
return;
|
|
3719
6385
|
}
|
|
3720
|
-
const fullPath =
|
|
3721
|
-
if (!
|
|
6386
|
+
const fullPath = resolve5(process.cwd(), filePath);
|
|
6387
|
+
if (!existsSync11(fullPath)) {
|
|
3722
6388
|
console.log(theme.error(` File not found: ${filePath}`));
|
|
3723
6389
|
console.log("");
|
|
3724
6390
|
return;
|
|
3725
6391
|
}
|
|
3726
6392
|
try {
|
|
3727
|
-
const content =
|
|
6393
|
+
const content = readFileSync9(fullPath, "utf-8");
|
|
3728
6394
|
const fileLines = content.split("\n").slice(0, lines);
|
|
3729
|
-
console.log(theme.dim(` First ${lines} lines of ${
|
|
6395
|
+
console.log(theme.dim(` First ${lines} lines of ${basename4(filePath)}:`));
|
|
3730
6396
|
console.log("");
|
|
3731
6397
|
fileLines.forEach((line, i) => {
|
|
3732
|
-
console.log(` ${
|
|
6398
|
+
console.log(` ${chalk5.dim((i + 1).toString().padStart(4))} ${line}`);
|
|
3733
6399
|
});
|
|
3734
6400
|
} catch {
|
|
3735
6401
|
console.log(theme.error(" Failed to read file"));
|
|
@@ -3761,8 +6427,9 @@ async function executeShellCommand(command) {
|
|
|
3761
6427
|
return;
|
|
3762
6428
|
}
|
|
3763
6429
|
if (cmd === "clear" || cmd === "cls") {
|
|
3764
|
-
|
|
3765
|
-
|
|
6430
|
+
process.stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
6431
|
+
const isSimple = process.argv.includes("--simple") || !process.stdout.isTTY;
|
|
6432
|
+
await displayBanner(isSimple);
|
|
3766
6433
|
return;
|
|
3767
6434
|
}
|
|
3768
6435
|
if (cmd === "bookmark" || cmd === "bm") {
|
|
@@ -3875,6 +6542,29 @@ async function parseAndExecute(input) {
|
|
|
3875
6542
|
return;
|
|
3876
6543
|
}
|
|
3877
6544
|
if (!trimmed.startsWith("/")) {
|
|
6545
|
+
const detectedMood = getMoodFromText(trimmed);
|
|
6546
|
+
if (detectedMood) {
|
|
6547
|
+
console.log("");
|
|
6548
|
+
const responses = {
|
|
6549
|
+
love: ["Aww, you're sweet!", "Right back at ya!", "*happy wobble*"],
|
|
6550
|
+
excited: ["Yay! Let's gooo!", "Woohoo!", "*bounces excitedly*"],
|
|
6551
|
+
sleepy: ["*yawns* Same...", "Maybe take a break?", "zzZ..."],
|
|
6552
|
+
sad: ["Aww, it's okay!", "I'm here for you!", "*comforting wobble*"],
|
|
6553
|
+
angry: ["*hides nervously*", "Let's fix that!", "Deep breaths..."],
|
|
6554
|
+
thinking: ["Hmm indeed...", "Let me think too...", "*ponders*"]
|
|
6555
|
+
};
|
|
6556
|
+
const moodResponses = responses[detectedMood] || ["*wobbles*"];
|
|
6557
|
+
const response = moodResponses[Math.floor(Math.random() * moodResponses.length)];
|
|
6558
|
+
const isSimpleInput = trimmed.length < 20 && !/[{}\[\]();=<>]/.test(trimmed);
|
|
6559
|
+
if ((detectedMood === "love" || detectedMood === "excited") && isSimpleInput && Math.random() < 0.5) {
|
|
6560
|
+
showMascot(detectedMood);
|
|
6561
|
+
console.log(` ${theme.secondary(response)}`);
|
|
6562
|
+
} else {
|
|
6563
|
+
react(detectedMood, theme.secondary(response));
|
|
6564
|
+
}
|
|
6565
|
+
console.log("");
|
|
6566
|
+
return;
|
|
6567
|
+
}
|
|
3878
6568
|
console.log("");
|
|
3879
6569
|
console.log(` ${symbols.info} ${theme.dim("Commands start with")} ${theme.primary("/")} ${theme.dim("\u2022 Shell commands start with")} ${theme.primary("!")}`);
|
|
3880
6570
|
console.log(` ${theme.dim(" Type")} ${theme.primary("/help")} ${theme.dim("for available commands")}`);
|
|
@@ -3894,24 +6584,110 @@ async function parseAndExecute(input) {
|
|
|
3894
6584
|
}
|
|
3895
6585
|
try {
|
|
3896
6586
|
await command.execute(args2);
|
|
6587
|
+
if (Math.random() < 0.15) {
|
|
6588
|
+
const successMoods = ["happy", "excited", "wink"];
|
|
6589
|
+
const mood = successMoods[Math.floor(Math.random() * successMoods.length)];
|
|
6590
|
+
react(mood);
|
|
6591
|
+
}
|
|
3897
6592
|
} catch (error) {
|
|
3898
6593
|
console.log("");
|
|
3899
|
-
|
|
6594
|
+
react("sad", theme.error(`Error: ${String(error)}`));
|
|
3900
6595
|
console.log("");
|
|
3901
6596
|
}
|
|
3902
6597
|
}
|
|
3903
6598
|
|
|
3904
6599
|
// src/index.ts
|
|
3905
|
-
import { readdirSync as
|
|
3906
|
-
import { fileURLToPath } from "url";
|
|
3907
|
-
import { dirname, join as
|
|
6600
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync10 } from "fs";
|
|
6601
|
+
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
6602
|
+
import { dirname as dirname5, join as join10 } from "path";
|
|
6603
|
+
import chalk6 from "chalk";
|
|
6604
|
+
var SLIME_COLOR = "#9B59B6";
|
|
6605
|
+
var EYE_COLOR = "#1A1A2E";
|
|
6606
|
+
var MIN_WIDTH_FOR_IDLE = 40;
|
|
6607
|
+
var MIN_WIDTH_FOR_MENU = 50;
|
|
6608
|
+
var miniSlimeFrames = [
|
|
6609
|
+
// Open eyes
|
|
6610
|
+
chalk6.hex(SLIME_COLOR)("(") + chalk6.hex(EYE_COLOR)("\u25CF") + chalk6.hex(SLIME_COLOR)("\u1D17") + chalk6.hex(EYE_COLOR)("\u25CF") + chalk6.hex(SLIME_COLOR)(")"),
|
|
6611
|
+
// Blink (closed eyes)
|
|
6612
|
+
chalk6.hex(SLIME_COLOR)("(") + chalk6.hex(EYE_COLOR)("\u2500") + chalk6.hex(SLIME_COLOR)("\u1D17") + chalk6.hex(EYE_COLOR)("\u2500") + chalk6.hex(SLIME_COLOR)(")")
|
|
6613
|
+
];
|
|
6614
|
+
var idleAnimationInterval = null;
|
|
6615
|
+
var isUserTyping = false;
|
|
6616
|
+
var lastActivityTime = Date.now();
|
|
6617
|
+
var lastRenderedFrame = -1;
|
|
6618
|
+
var slimeVisible = false;
|
|
6619
|
+
var lastTermWidth = process.stdout.columns || 80;
|
|
6620
|
+
var IDLE_DELAY = 3e3;
|
|
6621
|
+
var FRAME_INTERVAL = 2e3;
|
|
6622
|
+
var animationSequence = [0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0];
|
|
6623
|
+
var sequenceIndex = 0;
|
|
6624
|
+
function getAnimatedSlime() {
|
|
6625
|
+
const frameIndex = animationSequence[sequenceIndex % animationSequence.length];
|
|
6626
|
+
return { frame: miniSlimeFrames[frameIndex], index: frameIndex };
|
|
6627
|
+
}
|
|
6628
|
+
function updateIdleAnimation(rl) {
|
|
6629
|
+
const termWidth = process.stdout.columns || 80;
|
|
6630
|
+
if (isUserTyping || menu.visible || termWidth < MIN_WIDTH_FOR_IDLE) {
|
|
6631
|
+
if (slimeVisible) {
|
|
6632
|
+
clearIdleSlime(rl);
|
|
6633
|
+
}
|
|
6634
|
+
return;
|
|
6635
|
+
}
|
|
6636
|
+
const now = Date.now();
|
|
6637
|
+
if (now - lastActivityTime < IDLE_DELAY) return;
|
|
6638
|
+
const currentLine = rl.line || "";
|
|
6639
|
+
if (currentLine.length !== 0) return;
|
|
6640
|
+
sequenceIndex++;
|
|
6641
|
+
const { frame: slime, index: frameIndex } = getAnimatedSlime();
|
|
6642
|
+
if (frameIndex === lastRenderedFrame && slimeVisible) return;
|
|
6643
|
+
lastRenderedFrame = frameIndex;
|
|
6644
|
+
slimeVisible = true;
|
|
6645
|
+
const promptLen = getPromptLength();
|
|
6646
|
+
const slimePos = Math.max(promptLen + 3, termWidth - 12);
|
|
6647
|
+
process.stdout.write(
|
|
6648
|
+
`\x1B[?25l\x1B[${slimePos}G\x1B[K` + // Clear to end of line
|
|
6649
|
+
slime + // Draw slime
|
|
6650
|
+
`\x1B[${promptLen + 1}G\x1B[?25h`
|
|
6651
|
+
// Show cursor
|
|
6652
|
+
);
|
|
6653
|
+
}
|
|
6654
|
+
function startIdleAnimation(rl) {
|
|
6655
|
+
if (idleAnimationInterval) return;
|
|
6656
|
+
idleAnimationInterval = setInterval(() => {
|
|
6657
|
+
updateIdleAnimation(rl);
|
|
6658
|
+
}, FRAME_INTERVAL);
|
|
6659
|
+
}
|
|
6660
|
+
function stopIdleAnimation() {
|
|
6661
|
+
if (idleAnimationInterval) {
|
|
6662
|
+
clearInterval(idleAnimationInterval);
|
|
6663
|
+
idleAnimationInterval = null;
|
|
6664
|
+
}
|
|
6665
|
+
}
|
|
6666
|
+
function clearIdleSlime(rl) {
|
|
6667
|
+
if (!slimeVisible) return;
|
|
6668
|
+
slimeVisible = false;
|
|
6669
|
+
lastRenderedFrame = -1;
|
|
6670
|
+
const termWidth = process.stdout.columns || 80;
|
|
6671
|
+
const slimePos = termWidth - 12;
|
|
6672
|
+
const promptLen = getPromptLength();
|
|
6673
|
+
const currentLine = rl ? rl.line || "" : "";
|
|
6674
|
+
const cursorPos = promptLen + currentLine.length + 1;
|
|
6675
|
+
process.stdout.write(
|
|
6676
|
+
`\x1B[?25l\x1B[${slimePos}G\x1B[K\x1B[${cursorPos}G\x1B[?25h`
|
|
6677
|
+
// Show cursor
|
|
6678
|
+
);
|
|
6679
|
+
}
|
|
6680
|
+
function setIdle() {
|
|
6681
|
+
isUserTyping = false;
|
|
6682
|
+
lastActivityTime = Date.now();
|
|
6683
|
+
}
|
|
3908
6684
|
var args = process.argv.slice(2);
|
|
3909
6685
|
if (args.includes("--version") || args.includes("-v")) {
|
|
3910
6686
|
try {
|
|
3911
|
-
const __filename =
|
|
3912
|
-
const __dirname =
|
|
3913
|
-
const pkgPath =
|
|
3914
|
-
const pkg = JSON.parse(
|
|
6687
|
+
const __filename = fileURLToPath4(import.meta.url);
|
|
6688
|
+
const __dirname = dirname5(__filename);
|
|
6689
|
+
const pkgPath = join10(__dirname, "..", "package.json");
|
|
6690
|
+
const pkg = JSON.parse(readFileSync10(pkgPath, "utf-8"));
|
|
3915
6691
|
console.log(`zammy v${pkg.version}`);
|
|
3916
6692
|
} catch {
|
|
3917
6693
|
console.log("zammy v1.0.0");
|
|
@@ -3920,14 +6696,15 @@ if (args.includes("--version") || args.includes("-v")) {
|
|
|
3920
6696
|
}
|
|
3921
6697
|
if (args.includes("--help") || args.includes("-h")) {
|
|
3922
6698
|
console.log(`
|
|
3923
|
-
zammy -
|
|
6699
|
+
zammy - A feature-packed CLI with utilities, dev tools, and a bit of fun
|
|
3924
6700
|
|
|
3925
6701
|
Usage: zammy [options]
|
|
3926
6702
|
|
|
3927
6703
|
Options:
|
|
3928
6704
|
-v, --version Show version number
|
|
3929
6705
|
-h, --help Show this help message
|
|
3930
|
-
--simple Force simple mode (no
|
|
6706
|
+
--simple Force simple mode (no animations)
|
|
6707
|
+
--no-menu Disable interactive command menu
|
|
3931
6708
|
|
|
3932
6709
|
Commands:
|
|
3933
6710
|
Start zammy and type / to see all available commands
|
|
@@ -3942,6 +6719,7 @@ Examples:
|
|
|
3942
6719
|
}
|
|
3943
6720
|
var isTTY = process.stdin.isTTY && process.stdout.isTTY;
|
|
3944
6721
|
var isSimpleMode = process.argv.includes("--simple") || !isTTY;
|
|
6722
|
+
var noMenu = process.argv.includes("--no-menu");
|
|
3945
6723
|
var IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".gif", ".bmp", ".webp"];
|
|
3946
6724
|
var menu = {
|
|
3947
6725
|
visible: false,
|
|
@@ -4060,6 +6838,11 @@ function hideMenu(currentLine = "") {
|
|
|
4060
6838
|
menu.renderedLines = 0;
|
|
4061
6839
|
}
|
|
4062
6840
|
function showMenu(filter, prefix, currentLine) {
|
|
6841
|
+
const termWidth = process.stdout.columns || 80;
|
|
6842
|
+
if (termWidth < MIN_WIDTH_FOR_MENU) {
|
|
6843
|
+
if (menu.visible) hideMenu(currentLine);
|
|
6844
|
+
return;
|
|
6845
|
+
}
|
|
4063
6846
|
const allItems = prefix === "/" ? getFilteredCommands(filter) : getFilteredShellCommands(filter);
|
|
4064
6847
|
if (allItems.length === 0) {
|
|
4065
6848
|
if (menu.visible) hideMenu(currentLine);
|
|
@@ -4125,7 +6908,7 @@ function completer(line) {
|
|
|
4125
6908
|
const afterCommand = line.slice("/asciiart ".length);
|
|
4126
6909
|
const searchTerm = afterCommand.startsWith("@") ? afterCommand.slice(1) : afterCommand;
|
|
4127
6910
|
try {
|
|
4128
|
-
const files =
|
|
6911
|
+
const files = readdirSync5(process.cwd());
|
|
4129
6912
|
const imageFiles = files.filter((f) => {
|
|
4130
6913
|
const ext = f.toLowerCase().slice(f.lastIndexOf("."));
|
|
4131
6914
|
return IMAGE_EXTENSIONS.includes(ext);
|
|
@@ -4154,8 +6937,9 @@ async function main() {
|
|
|
4154
6937
|
console.log(theme.dim("Note: Running in simple mode (no TTY detected). Use Tab for autocomplete."));
|
|
4155
6938
|
console.log(theme.dim("For full features, run in a proper terminal or use: zammy --simple\n"));
|
|
4156
6939
|
}
|
|
4157
|
-
await displayBanner();
|
|
4158
|
-
|
|
6940
|
+
await displayBanner(isSimpleMode);
|
|
6941
|
+
await initPluginLoader();
|
|
6942
|
+
const rl = readline4.createInterface({
|
|
4159
6943
|
input: process.stdin,
|
|
4160
6944
|
output: process.stdout,
|
|
4161
6945
|
terminal: isTTY,
|
|
@@ -4163,8 +6947,29 @@ async function main() {
|
|
|
4163
6947
|
});
|
|
4164
6948
|
let prevLine = "";
|
|
4165
6949
|
if (isTTY && !isSimpleMode) {
|
|
6950
|
+
startIdleAnimation(rl);
|
|
6951
|
+
}
|
|
6952
|
+
if (isTTY) {
|
|
6953
|
+
process.stdout.on("resize", () => {
|
|
6954
|
+
const currentLine = rl.line || "";
|
|
6955
|
+
const newWidth = process.stdout.columns || 80;
|
|
6956
|
+
if (menu.visible) {
|
|
6957
|
+
hideMenu(currentLine);
|
|
6958
|
+
}
|
|
6959
|
+
if (slimeVisible) {
|
|
6960
|
+
clearIdleSlime(rl);
|
|
6961
|
+
}
|
|
6962
|
+
process.stdout.write("\r" + getPrompt() + currentLine);
|
|
6963
|
+
lastTermWidth = newWidth;
|
|
6964
|
+
});
|
|
6965
|
+
}
|
|
6966
|
+
if (isTTY && !noMenu) {
|
|
4166
6967
|
process.stdin.on("keypress", (_char, key) => {
|
|
4167
6968
|
if (!key) return;
|
|
6969
|
+
if (!isSimpleMode) {
|
|
6970
|
+
isUserTyping = true;
|
|
6971
|
+
lastActivityTime = Date.now();
|
|
6972
|
+
}
|
|
4168
6973
|
const currentLine = rl.line || "";
|
|
4169
6974
|
if (menu.visible) {
|
|
4170
6975
|
if (key.name === "up" || key.name === "down") {
|
|
@@ -4202,6 +7007,7 @@ async function main() {
|
|
|
4202
7007
|
const line = rl.line || "";
|
|
4203
7008
|
if (now - lastCtrlC < 500) {
|
|
4204
7009
|
hideMenu(line);
|
|
7010
|
+
stopIdleAnimation();
|
|
4205
7011
|
console.log("\n" + theme.secondary("Goodbye! See you next time.") + "\n");
|
|
4206
7012
|
process.exit(0);
|
|
4207
7013
|
} else {
|
|
@@ -4216,6 +7022,7 @@ async function main() {
|
|
|
4216
7022
|
hideMenu(input);
|
|
4217
7023
|
prevLine = "";
|
|
4218
7024
|
await parseAndExecute(input);
|
|
7025
|
+
if (!isSimpleMode) setIdle();
|
|
4219
7026
|
rl.prompt();
|
|
4220
7027
|
});
|
|
4221
7028
|
rl.prompt();
|