git-push-time 1.1.2 → 1.1.4
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/.github/workflows/npm-publish.yml +59 -32
- package/README.md +110 -112
- package/bin/index.js +321 -339
- package/lib/commands/edit-time.js +141 -141
- package/lib/commands/fix.js +113 -113
- package/lib/commands/github.js +429 -445
- package/lib/commands/pull.js +90 -90
- package/lib/commands/push.js +720 -720
- package/lib/config.js +220 -220
- package/lib/fix.js +286 -286
- package/lib/git.js +525 -516
- package/lib/github.js +322 -322
- package/lib/prompt-helpers.js +19 -20
- package/lib/secrets.js +92 -92
- package/lib/ui.js +1057 -1063
- package/lib/utils.js +120 -120
- package/package.json +36 -36
package/lib/ui.js
CHANGED
|
@@ -1,1063 +1,1057 @@
|
|
|
1
|
-
import chalk from "chalk";
|
|
2
|
-
import boxen from "boxen";
|
|
3
|
-
import stringWidth from "string-width";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if (
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
let
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
output.
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
const [
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
//
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
const
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
*
|
|
148
|
-
*
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
)
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
const
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
202
|
-
"
|
|
203
|
-
"WWKKKKKKKKKKKKKKWWWW....",
|
|
204
|
-
"
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
];
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
else if (top ===
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const
|
|
260
|
-
"
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
})
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
console.log(
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
function
|
|
329
|
-
|
|
330
|
-
const
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
console.log("");
|
|
397
|
-
console.log(
|
|
398
|
-
console.log(
|
|
399
|
-
if (
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
console.log(
|
|
404
|
-
console.log(
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
console.log(
|
|
410
|
-
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
boxKvDot("
|
|
436
|
-
|
|
437
|
-
if (user.
|
|
438
|
-
console.log(boxKvDot("
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
);
|
|
469
|
-
|
|
470
|
-
console.log("");
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
console.log(boxRow(
|
|
474
|
-
console.log(boxRow(
|
|
475
|
-
console.log(boxRow("", border));
|
|
476
|
-
console.log(boxRow(chalk.hex("#
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
console.log(
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
console.log(boxKvDot("
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
}
|
|
509
|
-
console.log(
|
|
510
|
-
console.log(
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
);
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
)
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
);
|
|
707
|
-
console.log(
|
|
708
|
-
boxRow(
|
|
709
|
-
chalk.hex("#
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
)
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
)
|
|
835
|
-
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
);
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
);
|
|
888
|
-
console.log(
|
|
889
|
-
boxRow(
|
|
890
|
-
chalk.hex("#E2E8F0")(" [
|
|
891
|
-
border,
|
|
892
|
-
),
|
|
893
|
-
);
|
|
894
|
-
console.log(
|
|
895
|
-
boxRow(
|
|
896
|
-
chalk.hex("#
|
|
897
|
-
border,
|
|
898
|
-
),
|
|
899
|
-
);
|
|
900
|
-
console.log(
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
);
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
);
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
)
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
boxRow(
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
),
|
|
994
|
-
);
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
);
|
|
998
|
-
|
|
999
|
-
boxRow(chalk.hex("#
|
|
1000
|
-
);
|
|
1001
|
-
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
const
|
|
1021
|
-
|
|
1022
|
-
chalk.hex("#
|
|
1023
|
-
chalk.hex("#64748B")(
|
|
1024
|
-
|
|
1025
|
-
)
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
})
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time -r <repo-url> -d "-2d" -y')} # Push tự động lùi 2 ngày
|
|
1059
|
-
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time fix')} # Quét & gỡ sạch secret
|
|
1060
|
-
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time pull')} # Cập nhật code mới từ remote
|
|
1061
|
-
`,
|
|
1062
|
-
);
|
|
1063
|
-
}
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
import boxen from "boxen";
|
|
3
|
+
import stringWidth from "string-width";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Shared frame width for the entire UI.
|
|
7
|
+
* - Keep dashboards, banners, prompts, and detail boxes aligned.
|
|
8
|
+
* - Shrink to `cols - 2` in narrow terminals and cap at 76 columns.
|
|
9
|
+
*/
|
|
10
|
+
export function getW() {
|
|
11
|
+
const cols = process.stdout.columns;
|
|
12
|
+
if (!cols || cols < 10) return 72;
|
|
13
|
+
const avail = cols - 2;
|
|
14
|
+
// On very narrow terminals (<34 columns), use the available width to prevent wrapping.
|
|
15
|
+
if (avail < 34) return Math.max(24, avail);
|
|
16
|
+
return Math.min(76, avail);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function fitStyled(text, maxWidth) {
|
|
20
|
+
const value = String(text ?? '').replace(/[\r\n\t]+/g, ' ');
|
|
21
|
+
if (maxWidth <= 0) return '';
|
|
22
|
+
if (stringWidth(value) <= maxWidth) return value;
|
|
23
|
+
|
|
24
|
+
const segmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme' });
|
|
25
|
+
const parts = value.match(/\u001b\[[0-?]*[ -/]*[@-~]|[^\u001b]+|\u001b/g) || [];
|
|
26
|
+
const output = [];
|
|
27
|
+
let visible = 0;
|
|
28
|
+
let clipped = false;
|
|
29
|
+
const target = Math.max(0, maxWidth - 1);
|
|
30
|
+
|
|
31
|
+
for (const part of parts) {
|
|
32
|
+
if (part.startsWith('\u001b[')) {
|
|
33
|
+
output.push(part);
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
for (const { segment } of segmenter.segment(part)) {
|
|
37
|
+
const width = stringWidth(segment);
|
|
38
|
+
if (visible + width > target) {
|
|
39
|
+
clipped = true;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
output.push(segment);
|
|
43
|
+
visible += width;
|
|
44
|
+
}
|
|
45
|
+
if (clipped) break;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
output.push('…');
|
|
49
|
+
// A clipped styled value must not leak its color into later terminal output.
|
|
50
|
+
if (value.includes('\u001b[')) output.push('\u001b[0m');
|
|
51
|
+
return output.join('');
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
function hexToRgb(hex) {
|
|
56
|
+
const n = parseInt(hex.replace("#", ""), 16);
|
|
57
|
+
return [(n >> 16) & 255, (n >> 8) & 255, n & 255];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function multiGradient(text, colors = ["#818CF8", "#38BDF8", "#34D399", "#A7F3D0"]) {
|
|
61
|
+
const chars = [...text];
|
|
62
|
+
const rgbColors = colors.map(hexToRgb);
|
|
63
|
+
return chars.map((char, index) => {
|
|
64
|
+
const t = (index / (chars.length - 1 || 1)) * (rgbColors.length - 1);
|
|
65
|
+
const colorIndex = Math.min(Math.floor(t), rgbColors.length - 2);
|
|
66
|
+
const localT = t - colorIndex;
|
|
67
|
+
const [r1, g1, b1] = rgbColors[colorIndex];
|
|
68
|
+
const [r2, g2, b2] = rgbColors[colorIndex + 1];
|
|
69
|
+
return chalk.rgb(Math.round(r1 + (r2-r1)*localT), Math.round(g1 + (g2-g1)*localT), Math.round(b1 + (b2-b1)*localT))(char);
|
|
70
|
+
}).join("");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function pad(str, len) {
|
|
74
|
+
const vLen = stringWidth(str);
|
|
75
|
+
if (vLen >= len) return str;
|
|
76
|
+
return str + " ".repeat(len - vLen);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function truncateVisual(str, maxLen) {
|
|
80
|
+
return fitStyled(str, maxLen);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Consistent title frame for each interactive terminal step. */
|
|
84
|
+
export function framePrompt(message) {
|
|
85
|
+
// Use the same width as other boxes to keep frames aligned and prevent narrow prompts
|
|
86
|
+
// from breaking against a wider dashboard.
|
|
87
|
+
return boxen(String(message ?? ""), {
|
|
88
|
+
borderStyle: "round",
|
|
89
|
+
borderColor: "#818CF8",
|
|
90
|
+
padding: { left: 1, right: 1, top: 0, bottom: 0 },
|
|
91
|
+
width: getW(),
|
|
92
|
+
textAlignment: "center",
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/* ==========================================================================
|
|
97
|
+
SHARED BOX ENGINE (rounded corners and precise alignment)
|
|
98
|
+
========================================================================== */
|
|
99
|
+
|
|
100
|
+
export function boxTop(title = "", borderColor = chalk.hex("#334155")) {
|
|
101
|
+
const W = getW();
|
|
102
|
+
if (!title) return borderColor("╭" + "─".repeat(Math.max(2, W - 2)) + "╮");
|
|
103
|
+
|
|
104
|
+
// Center the title with even spacing on both sides: ╭──── Title ────╮
|
|
105
|
+
// Leave one space beside the title and at least two dashes on each side.
|
|
106
|
+
const maxTitle = Math.max(4, W - 8);
|
|
107
|
+
const fittedTitle = fitStyled(title, maxTitle);
|
|
108
|
+
const titleWidth = stringWidth(fittedTitle);
|
|
109
|
+
const remaining = Math.max(4, W - 2 - titleWidth - 2);
|
|
110
|
+
const leftLen = Math.ceil(remaining / 2);
|
|
111
|
+
const rightLen = Math.floor(remaining / 2);
|
|
112
|
+
return (
|
|
113
|
+
borderColor("╭" + "─".repeat(leftLen) + " ") +
|
|
114
|
+
fittedTitle +
|
|
115
|
+
borderColor(" " + "─".repeat(rightLen) + "╮")
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Left-aligned title frame matching the content margin of boxRow. */
|
|
120
|
+
export function boxTopLeft(title = "", borderColor = chalk.hex("#334155")) {
|
|
121
|
+
const W = getW();
|
|
122
|
+
if (!title) return borderColor("╭" + "─".repeat(Math.max(2, W - 2)) + "╮");
|
|
123
|
+
const fittedTitle = truncateVisual(title, W - 6);
|
|
124
|
+
const titleWidth = stringWidth(fittedTitle);
|
|
125
|
+
const rightDash = Math.max(0, W - titleWidth - 4);
|
|
126
|
+
return borderColor("╭ ") + fittedTitle + borderColor(" " + "─".repeat(rightDash) + "╮");
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function boxBottom(borderColor = chalk.hex("#334155")) {
|
|
130
|
+
const W = getW();
|
|
131
|
+
return borderColor("╰" + "─".repeat(Math.max(2, W - 2)) + "╯");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function boxRow(content = "", borderColor = chalk.hex("#334155")) {
|
|
135
|
+
const W = getW();
|
|
136
|
+
const inner = Math.max(4, W - 4);
|
|
137
|
+
return borderColor("│ ") + pad(fitStyled(content, inner), inner) + borderColor(" │");
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function boxDivider(borderColor = chalk.hex("#334155")) {
|
|
141
|
+
const W = getW();
|
|
142
|
+
return borderColor("├" + "─".repeat(Math.max(2, W - 2)) + "┤");
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Key-value row connected with dot leaders.
|
|
147
|
+
* Use "·" (U+00B7) instead of "․" (U+2024) for compatibility with
|
|
148
|
+
* Windows PowerShell and Consolas. The label width adapts to narrow terminals.
|
|
149
|
+
*/
|
|
150
|
+
/**
|
|
151
|
+
* Key-value row with dot leaders, precise alignment, and consistent spacing.
|
|
152
|
+
*/
|
|
153
|
+
export function boxKvDot(
|
|
154
|
+
label,
|
|
155
|
+
value,
|
|
156
|
+
labelWidth = 14,
|
|
157
|
+
borderColor = chalk.hex("#334155"),
|
|
158
|
+
) {
|
|
159
|
+
const W = getW();
|
|
160
|
+
const innerWidth = Math.max(8, W - 4);
|
|
161
|
+
const fittedLabelWidth = Math.min(labelWidth, Math.max(4, innerWidth - 10));
|
|
162
|
+
const maxValWidth = Math.max(4, innerWidth - fittedLabelWidth - 4);
|
|
163
|
+
const truncatedVal = truncateVisual(value, maxValWidth);
|
|
164
|
+
const valWidth = stringWidth(truncatedVal);
|
|
165
|
+
|
|
166
|
+
const dotsLen = Math.max(2, innerWidth - fittedLabelWidth - valWidth - 2);
|
|
167
|
+
const dots = " " + chalk.hex("#334155")("·".repeat(dotsLen)) + " ";
|
|
168
|
+
const formattedLabel = chalk.hex("#94A3B8")(
|
|
169
|
+
pad(fitStyled(label, fittedLabelWidth), fittedLabelWidth),
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return boxRow(`${formattedLabel}${dots}${truncatedVal}`, borderColor);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* ==========================================================================
|
|
176
|
+
PUBLIC INTERFACE & BANNER
|
|
177
|
+
========================================================================== */
|
|
178
|
+
|
|
179
|
+
/* ==========================================================================
|
|
180
|
+
CYBER CAT MASCOT — 20x12 pixel art grid, two pixel rows per terminal cell
|
|
181
|
+
========================================================================== */
|
|
182
|
+
|
|
183
|
+
const MASCOT_COLORS = {
|
|
184
|
+
P: "#818CF8", // Indigo ears
|
|
185
|
+
W: "#F8FAFC", // White face
|
|
186
|
+
C: "#38BDF8", // Cyan visor
|
|
187
|
+
E: "#06B6D4", // Visor eye glow
|
|
188
|
+
K: "#0F172A", // Dark visor frame
|
|
189
|
+
M: "#F43F5E", // Rose nose/mouth
|
|
190
|
+
G: "#10B981", // Emerald badge
|
|
191
|
+
B: "#334155", // Slate base
|
|
192
|
+
".": "transparent",
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const MASCOT_GRID = [
|
|
196
|
+
"..PP............PP......",
|
|
197
|
+
".PPPP..........PPPP.....",
|
|
198
|
+
".PPWW..........WWPP.....",
|
|
199
|
+
"WWWWWWWWWWWWWWWWWWWW....",
|
|
200
|
+
"WWKKKKKKKKKKKKKKWWWW....",
|
|
201
|
+
"WWKCCCCCCCCCCCCKWWWW....",
|
|
202
|
+
"WWKCEKEEEEEEKECKWWWW....",
|
|
203
|
+
"WWKKKKKKKKKKKKKKWWWW....",
|
|
204
|
+
"WWWWWWWWMMWWWWWWWWWW....",
|
|
205
|
+
".WWWWWWWWWWWWWWWWWW..P..",
|
|
206
|
+
"..WWWW..GGGG..WWWWWWWW..",
|
|
207
|
+
"..BBBB..BBBB..BBBBWWWW..",
|
|
208
|
+
];
|
|
209
|
+
|
|
210
|
+
export function mascotLines() {
|
|
211
|
+
const lines = [];
|
|
212
|
+
for (let y = 0; y < MASCOT_GRID.length; y += 2) {
|
|
213
|
+
let line = "";
|
|
214
|
+
for (let x = 0; x < MASCOT_GRID[y].length; x++) {
|
|
215
|
+
const top = MASCOT_GRID[y][x];
|
|
216
|
+
const bot = MASCOT_GRID[y + 1][x];
|
|
217
|
+
if (top === "." && bot === ".") line += " ";
|
|
218
|
+
else if (top === "." || bot === ".") {
|
|
219
|
+
const pixel = top === "." ? bot : top;
|
|
220
|
+
line += chalk.hex(MASCOT_COLORS[pixel])(top === "." ? "▄" : "▀");
|
|
221
|
+
} else if (top === bot) {
|
|
222
|
+
line += chalk.hex(MASCOT_COLORS[top])("█");
|
|
223
|
+
} else {
|
|
224
|
+
line += chalk.hex(MASCOT_COLORS[top]).bgHex(MASCOT_COLORS[bot])("▀");
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
lines.push(line);
|
|
228
|
+
}
|
|
229
|
+
return lines;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Banner: Cyber Cat Mascot + Wordmark + Badges
|
|
234
|
+
*/
|
|
235
|
+
export function printBanner(version = "1.1.1") {
|
|
236
|
+
const W = getW();
|
|
237
|
+
const inner = Math.max(20, W - 2 - 4);
|
|
238
|
+
const badges = [
|
|
239
|
+
chalk.bgHex("#1E1B4B").hex("#818CF8").bold(` v${version} `),
|
|
240
|
+
chalk.bgHex("#0C4A6E").hex("#38BDF8").bold(" GIT WORKSPACE "),
|
|
241
|
+
];
|
|
242
|
+
const rows = [];
|
|
243
|
+
let row = "";
|
|
244
|
+
for (const badge of badges) {
|
|
245
|
+
const next = row ? `${row} ${badge}` : badge;
|
|
246
|
+
if (row && stringWidth(next) > inner) {
|
|
247
|
+
rows.push(row);
|
|
248
|
+
row = badge;
|
|
249
|
+
} else row = next;
|
|
250
|
+
}
|
|
251
|
+
if (row) rows.push(row);
|
|
252
|
+
|
|
253
|
+
const title = multiGradient("G I T W O R K S P A C E", [
|
|
254
|
+
"#818CF8",
|
|
255
|
+
"#38BDF8",
|
|
256
|
+
"#2DD4BF",
|
|
257
|
+
"#F472B6",
|
|
258
|
+
]);
|
|
259
|
+
const subtitle = chalk.hex("#94A3B8")(
|
|
260
|
+
"GitHub · Push / Pull · Commit history · Secret scan",
|
|
261
|
+
);
|
|
262
|
+
const mascot = mascotLines();
|
|
263
|
+
const body = [...mascot, "", title, subtitle, "", ...rows].join("\n");
|
|
264
|
+
const frame = boxen(body, {
|
|
265
|
+
borderStyle: "round",
|
|
266
|
+
borderColor: "#6366F1",
|
|
267
|
+
padding: { top: 1, bottom: 1, left: 2, right: 2 },
|
|
268
|
+
width: W,
|
|
269
|
+
textAlignment: "center",
|
|
270
|
+
});
|
|
271
|
+
console.log(`\n${frame}\n`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Display a concise status dashboard at the top of the menu.
|
|
276
|
+
*/
|
|
277
|
+
export function printDashboardStatus({
|
|
278
|
+
targetDir = process.cwd(),
|
|
279
|
+
currentBranch = "main",
|
|
280
|
+
githubUser = null,
|
|
281
|
+
remoteUrl = null,
|
|
282
|
+
changes = 0,
|
|
283
|
+
lastCommit = null,
|
|
284
|
+
version = "1.1.1",
|
|
285
|
+
}) {
|
|
286
|
+
const border = chalk.hex("#6366F1");
|
|
287
|
+
const W = getW();
|
|
288
|
+
const remote = remoteUrl ? formatRemoteLabel(remoteUrl) : chalk.hex("#94A3B8")("Not linked");
|
|
289
|
+
|
|
290
|
+
const ghStatus = githubUser
|
|
291
|
+
? `${chalk.hex("#38BDF8").bold(`@${githubUser.login}`)} ${chalk.bgHex("#064E3B").hex("#34D399").bold(" [✓ CONNECTED] ")}`
|
|
292
|
+
: chalk.bgHex("#1E1B4B").hex("#A5B4FC").bold(" [○ SIGNED OUT] ");
|
|
293
|
+
|
|
294
|
+
const branchPill = changes === 0
|
|
295
|
+
? chalk.bgHex("#064E3B").hex("#34D399").bold(" [✓ CLEAN] ")
|
|
296
|
+
: chalk.bgHex("#78350F").hex("#FDE047").bold(` [● ${changes} CHANGES] `);
|
|
297
|
+
|
|
298
|
+
const branchDisplay = `${chalk.hex("#38BDF8").bold(currentBranch)} ${branchPill}`;
|
|
299
|
+
|
|
300
|
+
console.log("");
|
|
301
|
+
console.log(
|
|
302
|
+
boxTop(
|
|
303
|
+
`${chalk.bold.hex("#F8FAFC")(" GIT DASHBOARD ")} ${chalk.hex("#94A3B8")(`v${version}`)}`,
|
|
304
|
+
border,
|
|
305
|
+
),
|
|
306
|
+
);
|
|
307
|
+
console.log(boxRow("", border));
|
|
308
|
+
console.log(boxKvDot("Folder", chalk.hex("#F1F5F9").bold(targetDir), 14, border));
|
|
309
|
+
console.log(boxKvDot("Branch", branchDisplay, 14, border));
|
|
310
|
+
console.log(boxKvDot("GitHub", ghStatus, 14, border));
|
|
311
|
+
console.log(boxKvDot("Remote", remote, 14, border));
|
|
312
|
+
|
|
313
|
+
if (lastCommit && lastCommit.hash) {
|
|
314
|
+
const commitText = `${chalk.hex("#A78BFA").bold(lastCommit.hash)} ${chalk.hex("#CBD5E1")(truncateVisual(lastCommit.message || "", 26))} ${chalk.hex("#64748B")(`(${lastCommit.relativeTime || ""})`)}`;
|
|
315
|
+
console.log(boxKvDot("Latest commit", commitText, 14, border));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
console.log(boxRow("", border));
|
|
319
|
+
console.log(boxBottom(border));
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function formatRemoteLabel(url) {
|
|
323
|
+
const value = String(url || "").trim().replace(/\.git$/i, "");
|
|
324
|
+
const match = value.match(/(?:github\.com[:/])([^/]+\/[^/]+)$/i);
|
|
325
|
+
return match ? chalk.hex("#38BDF8")(match[1]) : chalk.hex("#CBD5E1")(value);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
export function printPushProgress({ current, total = 6, title, completed = [] }) {
|
|
329
|
+
if (process.stdout.isTTY) process.stdout.write("\u001b[2J\u001b[H");
|
|
330
|
+
const nodes = Array.from({ length: total }, (_, index) => {
|
|
331
|
+
const n = index + 1;
|
|
332
|
+
if (n < current) return chalk.hex("#34D399")("●");
|
|
333
|
+
if (n === current) return chalk.hex("#38BDF8").bold("●");
|
|
334
|
+
return chalk.hex("#475569")("○");
|
|
335
|
+
});
|
|
336
|
+
const rail = nodes.join(chalk.hex("#334155")("──"));
|
|
337
|
+
const lead = `${chalk.hex("#38BDF8").bold("PUSH PROGRESS")} ${rail}`;
|
|
338
|
+
const detail = chalk.hex("#94A3B8")(`[Step ${current}/${total} · ${title}]`);
|
|
339
|
+
console.log("");
|
|
340
|
+
if (stringWidth(`${lead} ${detail}`) <= getW() - 2) console.log(`${lead} ${detail}`);
|
|
341
|
+
else {
|
|
342
|
+
console.log(lead);
|
|
343
|
+
console.log(` ${detail}`);
|
|
344
|
+
}
|
|
345
|
+
if (completed.length) {
|
|
346
|
+
console.log("");
|
|
347
|
+
for (const item of completed) {
|
|
348
|
+
console.log(
|
|
349
|
+
` ${chalk.hex("#34D399")("✓")} ${chalk.hex("#94A3B8")(item.label.padEnd(16))} ${chalk.hex("#334155")("·····")} ${chalk.hex("#F1F5F9")(truncateVisual(item.value, getW() - 28))}`,
|
|
350
|
+
);
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Section heading with a color accent.
|
|
357
|
+
*/
|
|
358
|
+
export function printSection(title) {
|
|
359
|
+
const W = getW();
|
|
360
|
+
const marker = chalk.hex("#A78BFA").bold("■");
|
|
361
|
+
const maxTitle = Math.max(4, W - 12);
|
|
362
|
+
const fittedTitle = fitStyled(String(title || "").trim(), maxTitle);
|
|
363
|
+
const coreWidth = stringWidth(fittedTitle) + 4;
|
|
364
|
+
const remaining = Math.max(4, W - coreWidth);
|
|
365
|
+
const leftLen = Math.ceil(remaining / 2);
|
|
366
|
+
const rightLen = Math.floor(remaining / 2);
|
|
367
|
+
console.log("");
|
|
368
|
+
console.log(
|
|
369
|
+
chalk.hex("#334155")("─".repeat(leftLen) + " ") +
|
|
370
|
+
marker +
|
|
371
|
+
chalk.bold.hex("#E2E8F0")(` ${fittedTitle} `) +
|
|
372
|
+
chalk.hex("#334155")("─".repeat(rightLen)),
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Step badge (for example, 1/6).
|
|
378
|
+
*/
|
|
379
|
+
export function stepLabel(current, total, text) {
|
|
380
|
+
const pill = chalk.bgHex("#2563EB").hex("#FFFFFF").bold(` ${current}/${total} `);
|
|
381
|
+
return `${pill} ${chalk.hex("#F1F5F9").bold(text)}`;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Summary panel displayed before pushing.
|
|
386
|
+
*/
|
|
387
|
+
export function printSummary({ repoUrl, commitMsg, dateDisplay, branch, force, sourceDir }) {
|
|
388
|
+
const border = chalk.hex("#38BDF8");
|
|
389
|
+
const title = chalk.bold.hex("#F8FAFC")(" REVIEW PUSH DETAILS ");
|
|
390
|
+
console.log("");
|
|
391
|
+
console.log(boxTop(title, border));
|
|
392
|
+
console.log(boxRow("", border));
|
|
393
|
+
if (sourceDir) console.log(boxKvDot("Source folder", chalk.hex("#F1F5F9")(sourceDir), 16, border));
|
|
394
|
+
console.log(boxKvDot("Repository", formatRemoteLabel(repoUrl), 16, border));
|
|
395
|
+
console.log(boxKvDot("Target branch", chalk.hex("#38BDF8").bold(branch), 16, border));
|
|
396
|
+
console.log(boxKvDot("Message", chalk.hex("#FDE047")(`"${commitMsg}"`), 16, border));
|
|
397
|
+
console.log(boxKvDot("Commit time", chalk.hex("#A78BFA")(dateDisplay), 16, border));
|
|
398
|
+
console.log(boxKvDot("Secret scan", chalk.hex("#34D399")("✓ Automatically checked before push"), 16, border));
|
|
399
|
+
if (force) {
|
|
400
|
+
console.log(boxRow("", border));
|
|
401
|
+
console.log(boxRow(chalk.hex("#F43F5E").bold(" [!] WARNING: Force push (--force) is enabled!"), border));
|
|
402
|
+
}
|
|
403
|
+
console.log(boxRow("", border));
|
|
404
|
+
console.log(boxBottom(border));
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
export function printTargetDir(dir = "") {
|
|
408
|
+
const W = getW();
|
|
409
|
+
console.log(
|
|
410
|
+
chalk.hex("#64748B")(
|
|
411
|
+
` Folder: ${truncateVisual(String(dir || ""), W - 13)}`,
|
|
412
|
+
),
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Display the GitHub account profile card.
|
|
418
|
+
*/
|
|
419
|
+
export function printGitHubProfile(user, source = '') {
|
|
420
|
+
const border = chalk.hex("#4F46E5");
|
|
421
|
+
const title = chalk.bold.bgHex("#4F46E5").hex("#FFFFFF")(
|
|
422
|
+
" CONNECTED GITHUB ACCOUNT ",
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
console.log("");
|
|
426
|
+
console.log(boxTop(title, border));
|
|
427
|
+
|
|
428
|
+
console.log(
|
|
429
|
+
boxKvDot("Username", chalk.hex("#38BDF8").bold(`@${user.login}`), 16, border)
|
|
430
|
+
);
|
|
431
|
+
if (user.name) {
|
|
432
|
+
console.log(boxKvDot("Display name", chalk.hex("#F8FAFC")(user.name), 16, border));
|
|
433
|
+
}
|
|
434
|
+
if (user.email) {
|
|
435
|
+
console.log(boxKvDot("Email", chalk.hex("#94A3B8")(user.email), 16, border));
|
|
436
|
+
}
|
|
437
|
+
if (user.bio) {
|
|
438
|
+
console.log(boxKvDot("Bio", chalk.hex("#CBD5E1")(truncateVisual(user.bio, 40)), 16, border));
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const repoStats = `${user.public_repos || 0} public • ${user.total_private_repos || 0} private`;
|
|
442
|
+
console.log(boxKvDot("Repositories", chalk.hex("#34D399").bold(repoStats), 16, border));
|
|
443
|
+
|
|
444
|
+
if (source) {
|
|
445
|
+
const srcDesc = source === 'gh-cli' ? 'GitHub CLI (gh)' : source === 'env' ? 'Environment variable' : 'Local config';
|
|
446
|
+
console.log(boxKvDot("Auth source", chalk.hex("#A78BFA")(srcDesc), 16, border));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
console.log(boxDivider(border));
|
|
450
|
+
console.log(boxRow(chalk.hex("#64748B")(`» Profile: ${user.html_url}`), border));
|
|
451
|
+
console.log(boxBottom(border));
|
|
452
|
+
console.log("");
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Display the GitHub Device Flow verification code.
|
|
457
|
+
*/
|
|
458
|
+
export function printDeviceCodePrompt({ userCode, verificationUri }) {
|
|
459
|
+
const border = chalk.hex("#6366F1");
|
|
460
|
+
const title = chalk.bold.bgHex("#4F46E5").hex("#FFFFFF")(
|
|
461
|
+
" GITHUB OAUTH / DEVICE FLOW ",
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
console.log("");
|
|
465
|
+
console.log(boxTop(title, border));
|
|
466
|
+
console.log(boxRow("", border));
|
|
467
|
+
console.log(boxRow(chalk.hex("#F8FAFC").bold("Step 1: Open this link in your browser:"), border));
|
|
468
|
+
console.log(boxRow(chalk.hex("#38BDF8").bold.underline(` ${verificationUri}`), border));
|
|
469
|
+
console.log(boxRow("", border));
|
|
470
|
+
console.log(boxRow(chalk.hex("#F8FAFC").bold("Step 2: Enter this user code:"), border));
|
|
471
|
+
|
|
472
|
+
const codeBox = chalk.bgHex("#1E1B4B").hex("#A5B4FC").bold(` >>> ${userCode} <<< `);
|
|
473
|
+
console.log(boxRow(` ${codeBox}`, border));
|
|
474
|
+
console.log(boxRow("", border));
|
|
475
|
+
console.log(boxRow(chalk.hex("#94A3B8")("» Your browser is opening automatically..."), border));
|
|
476
|
+
console.log(boxRow(chalk.hex("#64748B")("» Select Authorize in your browser to finish."), border));
|
|
477
|
+
console.log(boxBottom(border));
|
|
478
|
+
console.log("");
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Display a success message after creating a GitHub repository.
|
|
483
|
+
*/
|
|
484
|
+
export function printRepoCreatedBox(repo) {
|
|
485
|
+
const border = chalk.hex("#059669");
|
|
486
|
+
const title = chalk.bold.bgHex("#10B981").hex("#064E3B")(
|
|
487
|
+
" GITHUB REPOSITORY CREATED ",
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
console.log("");
|
|
491
|
+
console.log(boxTop(title, border));
|
|
492
|
+
console.log(boxRow("", border));
|
|
493
|
+
|
|
494
|
+
const visibilityPill = repo.isPrivate
|
|
495
|
+
? chalk.bgHex("#7F1D1D").hex("#FCA5A5").bold(" PRIVATE ")
|
|
496
|
+
: chalk.bgHex("#065F46").hex("#6EE7B7").bold(" PUBLIC ");
|
|
497
|
+
|
|
498
|
+
console.log(boxKvDot("Repository", chalk.hex("#F8FAFC").bold(repo.fullName || repo.name), 14, border));
|
|
499
|
+
console.log(boxKvDot("Visibility", visibilityPill, 14, border));
|
|
500
|
+
if (repo.description) {
|
|
501
|
+
console.log(boxKvDot("Description", chalk.hex("#CBD5E1")(truncateVisual(repo.description, 42)), 14, border));
|
|
502
|
+
}
|
|
503
|
+
console.log(boxKvDot("HTTPS Clone", chalk.hex("#38BDF8").underline(repo.cloneUrl), 14, border));
|
|
504
|
+
console.log(boxKvDot("SSH Clone", chalk.hex("#A78BFA")(repo.sshUrl), 14, border));
|
|
505
|
+
|
|
506
|
+
console.log(boxRow("", border));
|
|
507
|
+
console.log(boxDivider(border));
|
|
508
|
+
console.log(boxRow(chalk.hex("#6EE7B7")(`» View on GitHub: ${repo.htmlUrl}`), border));
|
|
509
|
+
console.log(boxBottom(border));
|
|
510
|
+
console.log("");
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
/**
|
|
514
|
+
* Warn when a folder is nested inside a parent repository.
|
|
515
|
+
*/
|
|
516
|
+
export function printNestedRepoWarning({ targetDir = "", topLevel = "" } = {}) {
|
|
517
|
+
const border = chalk.hex("#D97706");
|
|
518
|
+
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
519
|
+
" FOLDER INSIDE A PARENT REPOSITORY ",
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
console.log("");
|
|
523
|
+
console.log(boxTop(title, border));
|
|
524
|
+
console.log(
|
|
525
|
+
boxRow(
|
|
526
|
+
chalk.hex("#FDE047")(
|
|
527
|
+
"The source folder is inside the parent Git repository:",
|
|
528
|
+
),
|
|
529
|
+
border,
|
|
530
|
+
),
|
|
531
|
+
);
|
|
532
|
+
console.log(boxKvDot(" +- CHA", chalk.hex("#F8FAFC")(topLevel), 10, border));
|
|
533
|
+
console.log(
|
|
534
|
+
boxKvDot(" +- CON", chalk.hex("#F8FAFC")(targetDir), 10, border),
|
|
535
|
+
);
|
|
536
|
+
console.log(boxRow("", border));
|
|
537
|
+
console.log(
|
|
538
|
+
boxRow(
|
|
539
|
+
chalk.hex("#94A3B8")(
|
|
540
|
+
"Pushing may include the parent folder's history and files.",
|
|
541
|
+
),
|
|
542
|
+
border,
|
|
543
|
+
),
|
|
544
|
+
);
|
|
545
|
+
console.log(
|
|
546
|
+
boxRow(
|
|
547
|
+
chalk.hex("#34D399")(
|
|
548
|
+
"Tip: Create a SEPARATE repository to push only this folder.",
|
|
549
|
+
),
|
|
550
|
+
border,
|
|
551
|
+
),
|
|
552
|
+
);
|
|
553
|
+
console.log(boxBottom(border));
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/**
|
|
557
|
+
* Warn about sensitive secrets.
|
|
558
|
+
*/
|
|
559
|
+
export function printPrePushSecretWarning({ riskyFiles = [], findings = [] }) {
|
|
560
|
+
const border = chalk.hex("#D97706");
|
|
561
|
+
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
562
|
+
" SENSITIVE DATA DETECTED ",
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
console.log("");
|
|
566
|
+
console.log(boxTop(title, border));
|
|
567
|
+
console.log(
|
|
568
|
+
boxRow(
|
|
569
|
+
chalk.hex("#FDE047")(
|
|
570
|
+
"GitHub may block the push if it detects a secret (GH013)",
|
|
571
|
+
),
|
|
572
|
+
border,
|
|
573
|
+
),
|
|
574
|
+
);
|
|
575
|
+
console.log(boxRow("", border));
|
|
576
|
+
|
|
577
|
+
const W2 = getW();
|
|
578
|
+
const maxFileW = Math.max(10, W2 - 14);
|
|
579
|
+
const maxLocW = Math.max(10, W2 - 24);
|
|
580
|
+
const maxPrevW = Math.max(10, W2 - 18);
|
|
581
|
+
|
|
582
|
+
if (riskyFiles.length > 0) {
|
|
583
|
+
console.log(
|
|
584
|
+
boxRow(chalk.bold.hex("#F8FAFC")("Sensitive files:"), border),
|
|
585
|
+
);
|
|
586
|
+
const showFiles = riskyFiles.slice(0, 4);
|
|
587
|
+
showFiles.forEach((f) => {
|
|
588
|
+
const tree = chalk.hex("#64748B")(" +- ");
|
|
589
|
+
console.log(
|
|
590
|
+
boxRow(tree + chalk.hex("#FCD34D")(truncateVisual(f, maxFileW)), border),
|
|
591
|
+
);
|
|
592
|
+
});
|
|
593
|
+
if (riskyFiles.length > 4) {
|
|
594
|
+
console.log(
|
|
595
|
+
boxRow(
|
|
596
|
+
chalk.hex("#64748B")(` +- ...and ${riskyFiles.length - 4} more file(s)`),
|
|
597
|
+
border,
|
|
598
|
+
),
|
|
599
|
+
);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
if (findings.length > 0) {
|
|
604
|
+
if (riskyFiles.length > 0) console.log(boxRow("", border));
|
|
605
|
+
console.log(
|
|
606
|
+
boxRow(
|
|
607
|
+
chalk.bold.hex("#F8FAFC")("Possible secrets in source files:"),
|
|
608
|
+
border,
|
|
609
|
+
),
|
|
610
|
+
);
|
|
611
|
+
const showFindings = findings.slice(0, 4);
|
|
612
|
+
showFindings.forEach((f, idx) => {
|
|
613
|
+
const isLast = idx === showFindings.length - 1;
|
|
614
|
+
const tree = chalk.hex("#64748B")(" +- ");
|
|
615
|
+
const loc = truncateVisual(`${f.file}:${f.line}`, maxLocW);
|
|
616
|
+
console.log(
|
|
617
|
+
boxRow(
|
|
618
|
+
tree +
|
|
619
|
+
chalk.hex("#F8FAFC")(loc) +
|
|
620
|
+
chalk.hex("#94A3B8")(` [${f.type}]`),
|
|
621
|
+
border,
|
|
622
|
+
),
|
|
623
|
+
);
|
|
624
|
+
if (f.preview) {
|
|
625
|
+
const indent = chalk.hex("#64748B")(isLast ? " " : " | ");
|
|
626
|
+
console.log(
|
|
627
|
+
boxRow(
|
|
628
|
+
indent + chalk.hex("#64748B")(`"${truncateVisual(f.preview, maxPrevW)}"`),
|
|
629
|
+
border,
|
|
630
|
+
),
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
if (findings.length > 4) {
|
|
635
|
+
console.log(
|
|
636
|
+
boxRow(
|
|
637
|
+
chalk.hex("#64748B")(` +- ...and ${findings.length - 4} more location(s)`),
|
|
638
|
+
border,
|
|
639
|
+
),
|
|
640
|
+
);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
console.log(boxRow("", border));
|
|
645
|
+
console.log(
|
|
646
|
+
boxRow(
|
|
647
|
+
chalk.hex("#34D399")(
|
|
648
|
+
'Tip: Choose "Auto-fix" to safely unstage files and update .gitignore',
|
|
649
|
+
),
|
|
650
|
+
border,
|
|
651
|
+
),
|
|
652
|
+
);
|
|
653
|
+
console.log(boxBottom(border));
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/**
|
|
657
|
+
* Warn when GitHub Push Protection blocks a push (GH013).
|
|
658
|
+
*/
|
|
659
|
+
export function printSecretBlockedBox(rawMessage, details = {}) {
|
|
660
|
+
const border = chalk.hex("#E11D48");
|
|
661
|
+
const files = details.files || [];
|
|
662
|
+
const types = details.secretTypes || [];
|
|
663
|
+
const title = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(
|
|
664
|
+
" PUSH BLOCKED — SECRET DETECTED (GH013) ",
|
|
665
|
+
);
|
|
666
|
+
|
|
667
|
+
console.log("");
|
|
668
|
+
console.log(boxTop(title, border));
|
|
669
|
+
console.log(
|
|
670
|
+
boxRow(
|
|
671
|
+
chalk.hex("#FDA4AF")(
|
|
672
|
+
"GitHub Push Protection blocked the push because it detected a secret.",
|
|
673
|
+
),
|
|
674
|
+
border,
|
|
675
|
+
),
|
|
676
|
+
);
|
|
677
|
+
console.log(boxRow("", border));
|
|
678
|
+
|
|
679
|
+
if (types.length > 0) {
|
|
680
|
+
console.log(
|
|
681
|
+
boxKvDot(
|
|
682
|
+
"Secret type",
|
|
683
|
+
chalk.hex("#F8FAFC").bold(types.slice(0, 2).join(", ")),
|
|
684
|
+
14,
|
|
685
|
+
border,
|
|
686
|
+
),
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
if (files.length > 0) {
|
|
690
|
+
files.slice(0, 2).forEach((f) => {
|
|
691
|
+
console.log(
|
|
692
|
+
boxKvDot("Source file", chalk.hex("#FCD34D")(f), 14, border),
|
|
693
|
+
);
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
console.log(boxRow("", border));
|
|
698
|
+
console.log(
|
|
699
|
+
boxRow(chalk.bold.hex("#F8FAFC")("» How to resolve this:"), border),
|
|
700
|
+
);
|
|
701
|
+
console.log(
|
|
702
|
+
boxRow(
|
|
703
|
+
chalk.hex("#E2E8F0")(" [1] Revoke the key immediately if it is real"),
|
|
704
|
+
border,
|
|
705
|
+
),
|
|
706
|
+
);
|
|
707
|
+
console.log(
|
|
708
|
+
boxRow(
|
|
709
|
+
chalk.hex("#38BDF8")(
|
|
710
|
+
" [2] Use this tool to unstage files and squash the commits",
|
|
711
|
+
),
|
|
712
|
+
border,
|
|
713
|
+
),
|
|
714
|
+
);
|
|
715
|
+
console.log(
|
|
716
|
+
boxRow(
|
|
717
|
+
chalk.hex("#94A3B8")(
|
|
718
|
+
" [3] Remove the secret from source code and use an environment variable (process.env)",
|
|
719
|
+
),
|
|
720
|
+
border,
|
|
721
|
+
),
|
|
722
|
+
);
|
|
723
|
+
|
|
724
|
+
console.log(boxBottom(border));
|
|
725
|
+
|
|
726
|
+
if (details.unblockUrl) {
|
|
727
|
+
console.log(
|
|
728
|
+
chalk.hex("#F59E0B")(
|
|
729
|
+
`\n » Temporary unblock URL (for test credentials only):\n ${chalk.underline(details.unblockUrl)}`,
|
|
730
|
+
),
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
console.log(
|
|
734
|
+
chalk.hex("#64748B")(
|
|
735
|
+
`\n » GitHub documentation: ${details.resolveUrl || "https://docs.github.com/code-security/secret-scanning/pushing-a-branch-blocked-by-push-protection"}\n`,
|
|
736
|
+
),
|
|
737
|
+
);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Success card displayed after a push.
|
|
742
|
+
*/
|
|
743
|
+
/**
|
|
744
|
+
* Success card displayed after a push.
|
|
745
|
+
*/
|
|
746
|
+
export function printSuccessBox({
|
|
747
|
+
branch,
|
|
748
|
+
repoUrl,
|
|
749
|
+
durationSeconds = null,
|
|
750
|
+
secretStatus = "Scanned (no risks)",
|
|
751
|
+
}) {
|
|
752
|
+
const border = chalk.hex("#10B981");
|
|
753
|
+
const title = chalk.bold.hex("#F8FAFC")(" PUSH SUCCEEDED ");
|
|
754
|
+
console.log("");
|
|
755
|
+
console.log(boxTop(title, border));
|
|
756
|
+
console.log(boxRow("", border));
|
|
757
|
+
console.log(boxKvDot("Target branch", chalk.hex("#34D399").bold(branch), 18, border));
|
|
758
|
+
console.log(boxKvDot("Secret scan", chalk.hex("#38BDF8")(secretStatus), 18, border));
|
|
759
|
+
if (durationSeconds !== null) {
|
|
760
|
+
console.log(
|
|
761
|
+
boxKvDot(
|
|
762
|
+
"Duration",
|
|
763
|
+
chalk.hex("#FBBF24").bold(`${durationSeconds.toFixed(1)}s`),
|
|
764
|
+
18,
|
|
765
|
+
border,
|
|
766
|
+
),
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
const webUrl = toGitHubWebUrl(repoUrl);
|
|
770
|
+
if (webUrl) {
|
|
771
|
+
console.log(boxRow("", border));
|
|
772
|
+
console.log(boxDivider(border));
|
|
773
|
+
console.log(
|
|
774
|
+
boxRow(
|
|
775
|
+
`${chalk.hex("#34D399")("» View on GitHub:")} ${chalk.hex("#38BDF8").underline(webUrl)}`,
|
|
776
|
+
border,
|
|
777
|
+
),
|
|
778
|
+
);
|
|
779
|
+
} else {
|
|
780
|
+
console.log(boxKvDot("Remote", chalk.hex("#CBD5E1")(truncateVisual(repoUrl, getW() - 14)), 18, border));
|
|
781
|
+
}
|
|
782
|
+
console.log(boxRow("", border));
|
|
783
|
+
console.log(boxBottom(border));
|
|
784
|
+
console.log("");
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function toGitHubWebUrl(url) {
|
|
788
|
+
const match = String(url || "").trim().match(/(?:https?:\/\/github\.com\/|git@github\.com:|ssh:\/\/git@github\.com\/)([^/]+\/[^/]+?)(?:\.git)?$/i);
|
|
789
|
+
return match ? `https://github.com/${match[1]}` : null;
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
export function printCancelled() {
|
|
793
|
+
const border = chalk.hex("#64748B");
|
|
794
|
+
const title = chalk.bold.hex("#F8FAFC")(
|
|
795
|
+
" OPERATION CANCELLED ",
|
|
796
|
+
);
|
|
797
|
+
console.log("");
|
|
798
|
+
console.log(boxTop(title, border));
|
|
799
|
+
console.log(boxRow("", border));
|
|
800
|
+
console.log(
|
|
801
|
+
boxRow(
|
|
802
|
+
chalk.hex("#94A3B8")(
|
|
803
|
+
"The operation was safely stopped. No changes were made.",
|
|
804
|
+
),
|
|
805
|
+
border,
|
|
806
|
+
),
|
|
807
|
+
);
|
|
808
|
+
console.log(boxRow("", border));
|
|
809
|
+
console.log(boxBottom(border));
|
|
810
|
+
console.log("");
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
export function printErrorBox(title = "AN ERROR OCCURRED", lines = [], hint = "") {
|
|
814
|
+
const W = getW();
|
|
815
|
+
const border = chalk.hex("#E11D48");
|
|
816
|
+
const cleanTitle = String(title || "").toUpperCase();
|
|
817
|
+
const titleStyled = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(` [ERROR] ${cleanTitle} `);
|
|
818
|
+
const rows = Array.isArray(lines) ? lines : [String(lines || "")];
|
|
819
|
+
|
|
820
|
+
console.log("");
|
|
821
|
+
console.log(boxTop(titleStyled, border));
|
|
822
|
+
rows.forEach((l) => {
|
|
823
|
+
if (!l) {
|
|
824
|
+
console.log(boxRow("", border));
|
|
825
|
+
} else {
|
|
826
|
+
console.log(
|
|
827
|
+
boxRow(chalk.hex("#FDA4AF")(truncateVisual(String(l), W - 4)), border),
|
|
828
|
+
);
|
|
829
|
+
}
|
|
830
|
+
});
|
|
831
|
+
if (hint) {
|
|
832
|
+
console.log(boxRow("", border));
|
|
833
|
+
console.log(
|
|
834
|
+
boxRow(chalk.hex("#38BDF8")(`» ${truncateVisual(hint, W - 6)}`), border),
|
|
835
|
+
);
|
|
836
|
+
}
|
|
837
|
+
console.log(boxBottom(border));
|
|
838
|
+
console.log("");
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
export function printGitMissing() {
|
|
842
|
+
printErrorBox(
|
|
843
|
+
"GIT IS NOT INSTALLED",
|
|
844
|
+
[
|
|
845
|
+
"Git was not found on this system.",
|
|
846
|
+
"Download and install Git to continue.",
|
|
847
|
+
],
|
|
848
|
+
"Download it at: https://git-scm.com/downloads",
|
|
849
|
+
);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
export function printAuthFailedBox(message = "") {
|
|
853
|
+
const border = chalk.hex("#E11D48");
|
|
854
|
+
const title = chalk.bold.bgHex("#E11D48").hex("#FFFFFF")(
|
|
855
|
+
" GIT AUTHENTICATION FAILED ",
|
|
856
|
+
);
|
|
857
|
+
const firstLine = String(message || "")
|
|
858
|
+
.split("\n")[0]
|
|
859
|
+
.slice(0, 120);
|
|
860
|
+
|
|
861
|
+
console.log("");
|
|
862
|
+
console.log(boxTop(title, border));
|
|
863
|
+
console.log(
|
|
864
|
+
boxRow(
|
|
865
|
+
chalk.hex("#FDA4AF")(
|
|
866
|
+
"Could not authenticate with the remote. Check your access permissions.",
|
|
867
|
+
),
|
|
868
|
+
border,
|
|
869
|
+
),
|
|
870
|
+
);
|
|
871
|
+
if (firstLine)
|
|
872
|
+
console.log(
|
|
873
|
+
boxRow(
|
|
874
|
+
chalk.hex("#64748B")(truncateVisual(firstLine, getW() - 4)),
|
|
875
|
+
border,
|
|
876
|
+
),
|
|
877
|
+
);
|
|
878
|
+
console.log(boxRow("", border));
|
|
879
|
+
console.log(
|
|
880
|
+
boxRow(chalk.bold.hex("#F8FAFC")("» Quick fixes:"), border),
|
|
881
|
+
);
|
|
882
|
+
console.log(
|
|
883
|
+
boxRow(
|
|
884
|
+
chalk.hex("#E2E8F0")(" [1] Use the GITHUB menu to connect your account"),
|
|
885
|
+
border,
|
|
886
|
+
),
|
|
887
|
+
);
|
|
888
|
+
console.log(
|
|
889
|
+
boxRow(
|
|
890
|
+
chalk.hex("#E2E8F0")(" [2] Use GitHub CLI: gh auth login"),
|
|
891
|
+
border,
|
|
892
|
+
),
|
|
893
|
+
);
|
|
894
|
+
console.log(
|
|
895
|
+
boxRow(
|
|
896
|
+
chalk.hex("#94A3B8")(" [3] Use an SSH URL: git@github.com:user/repo.git"),
|
|
897
|
+
border,
|
|
898
|
+
),
|
|
899
|
+
);
|
|
900
|
+
console.log(boxBottom(border));
|
|
901
|
+
console.log("");
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
export function printNonFastForwardBox(message = "", branch = "main") {
|
|
905
|
+
const border = chalk.hex("#D97706");
|
|
906
|
+
const title = chalk.bold.bgHex("#D97706").hex("#FFFFFF")(
|
|
907
|
+
" REMOTE HAS NEWER COMMITS ",
|
|
908
|
+
);
|
|
909
|
+
const firstLine = String(message || "")
|
|
910
|
+
.split("\n")[0]
|
|
911
|
+
.slice(0, 120);
|
|
912
|
+
|
|
913
|
+
console.log("");
|
|
914
|
+
console.log(boxTop(title, border));
|
|
915
|
+
console.log(
|
|
916
|
+
boxRow(
|
|
917
|
+
chalk.hex("#FDE68A")(
|
|
918
|
+
"The remote repository has commits that are newer than your local branch.",
|
|
919
|
+
),
|
|
920
|
+
border,
|
|
921
|
+
),
|
|
922
|
+
);
|
|
923
|
+
if (firstLine)
|
|
924
|
+
console.log(
|
|
925
|
+
boxRow(
|
|
926
|
+
chalk.hex("#64748B")(truncateVisual(firstLine, getW() - 4)),
|
|
927
|
+
border,
|
|
928
|
+
),
|
|
929
|
+
);
|
|
930
|
+
console.log(boxRow("", border));
|
|
931
|
+
console.log(
|
|
932
|
+
boxRow(
|
|
933
|
+
chalk.hex("#38BDF8")(
|
|
934
|
+
` » Choose Pull (rebase), then push again from this tool.`,
|
|
935
|
+
),
|
|
936
|
+
border,
|
|
937
|
+
),
|
|
938
|
+
);
|
|
939
|
+
console.log(
|
|
940
|
+
boxRow(
|
|
941
|
+
chalk.hex("#94A3B8")(
|
|
942
|
+
" Use --force only when you are certain you want to overwrite the remote.",
|
|
943
|
+
),
|
|
944
|
+
border,
|
|
945
|
+
),
|
|
946
|
+
);
|
|
947
|
+
console.log(boxBottom(border));
|
|
948
|
+
console.log("");
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
export function printFixResult({
|
|
952
|
+
fixed = [],
|
|
953
|
+
gitignoreAdded = [],
|
|
954
|
+
codeFilesWithSecrets = [],
|
|
955
|
+
failed = [],
|
|
956
|
+
} = {}) {
|
|
957
|
+
const W = getW();
|
|
958
|
+
const border = chalk.hex("#10B981");
|
|
959
|
+
const title = chalk.bold.hex("#F8FAFC")(" SECRET PROTECTION RESULTS ");
|
|
960
|
+
|
|
961
|
+
console.log("");
|
|
962
|
+
console.log(boxTop(title, border));
|
|
963
|
+
console.log(boxRow("", border));
|
|
964
|
+
|
|
965
|
+
if (fixed.length > 0) {
|
|
966
|
+
console.log(
|
|
967
|
+
boxRow(
|
|
968
|
+
`${chalk.hex("#34D399").bold("[OK] Removed from commit:")} ${chalk.hex("#F1F5F9")(truncateVisual(fixed.join(", "), W - 28))}`,
|
|
969
|
+
border,
|
|
970
|
+
),
|
|
971
|
+
);
|
|
972
|
+
}
|
|
973
|
+
if (gitignoreAdded.length > 0) {
|
|
974
|
+
console.log(
|
|
975
|
+
boxRow(
|
|
976
|
+
`${chalk.hex("#38BDF8").bold("[+] Added to .gitignore:")} ${chalk.hex("#CBD5E1")(truncateVisual(gitignoreAdded.join(", "), W - 30))}`,
|
|
977
|
+
border,
|
|
978
|
+
),
|
|
979
|
+
);
|
|
980
|
+
}
|
|
981
|
+
if (codeFilesWithSecrets && codeFilesWithSecrets.length > 0) {
|
|
982
|
+
console.log(boxRow("", border));
|
|
983
|
+
console.log(
|
|
984
|
+
boxRow(
|
|
985
|
+
chalk.hex("#FBBF24").bold("[!] Source files containing secrets (unstaged, not ignored):"),
|
|
986
|
+
border,
|
|
987
|
+
),
|
|
988
|
+
);
|
|
989
|
+
codeFilesWithSecrets.forEach((f) =>
|
|
990
|
+
console.log(boxRow(` - ${chalk.hex("#CBD5E1")(truncateVisual(f, W - 10))}`, border)),
|
|
991
|
+
);
|
|
992
|
+
console.log(
|
|
993
|
+
boxRow(chalk.hex("#94A3B8")(" » Remove the key from the file and use process.env instead."), border),
|
|
994
|
+
);
|
|
995
|
+
}
|
|
996
|
+
if (failed.length > 0) {
|
|
997
|
+
console.log(boxRow("", border));
|
|
998
|
+
failed.forEach((f) =>
|
|
999
|
+
console.log(boxRow(chalk.hex("#F43F5E")(` [x] ${f.file}: ${f.reason}`), border)),
|
|
1000
|
+
);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
console.log(boxRow("", border));
|
|
1004
|
+
console.log(boxBottom(border));
|
|
1005
|
+
console.log("");
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
export function printFooter() {
|
|
1009
|
+
const W = getW();
|
|
1010
|
+
// Full-width divider, aligned like the other frames.
|
|
1011
|
+
const core = "»";
|
|
1012
|
+
const remaining = Math.max(4, W - 3); // 1 core + 2 space
|
|
1013
|
+
const leftLen = Math.ceil(remaining / 2);
|
|
1014
|
+
const rightLen = Math.floor(remaining / 2);
|
|
1015
|
+
console.log(
|
|
1016
|
+
chalk.hex("#334155")("─".repeat(leftLen) + " ") +
|
|
1017
|
+
chalk.hex("#64748B")(core) +
|
|
1018
|
+
chalk.hex("#334155")(" " + "─".repeat(rightLen))
|
|
1019
|
+
);
|
|
1020
|
+
const tip1 =
|
|
1021
|
+
chalk.hex("#64748B")("» Tip: Run ") +
|
|
1022
|
+
chalk.hex("#38BDF8").bold("git-push-time --help") +
|
|
1023
|
+
chalk.hex("#64748B")(" to see all commands.");
|
|
1024
|
+
const tip2 = chalk.hex("#475569")("» Git & GitHub workspace — Happy coding!");
|
|
1025
|
+
for (const tip of [tip1, tip2]) {
|
|
1026
|
+
const w = stringWidth(tip);
|
|
1027
|
+
if (w >= W) {
|
|
1028
|
+
console.log(` ${tip}`);
|
|
1029
|
+
} else {
|
|
1030
|
+
const padTotal = W - w;
|
|
1031
|
+
const left = Math.floor(padTotal / 2);
|
|
1032
|
+
console.log(" ".repeat(left) + tip);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
console.log("");
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
export function styleCliHelp(program) {
|
|
1039
|
+
program.configureHelp({
|
|
1040
|
+
styleTitle: (t) => chalk.bold.hex("#38BDF8")(t),
|
|
1041
|
+
styleCommandText: (t) => chalk.hex("#F8FAFC")(t),
|
|
1042
|
+
styleOptionText: (t) => chalk.hex("#34D399")(t),
|
|
1043
|
+
styleArgumentText: (t) => chalk.hex("#FBBF24")(t),
|
|
1044
|
+
styleDescriptionText: (t) => chalk.hex("#94A3B8")(t),
|
|
1045
|
+
});
|
|
1046
|
+
program.addHelpText(
|
|
1047
|
+
"after",
|
|
1048
|
+
`
|
|
1049
|
+
${chalk.hex("#334155")("─── » Common examples ───")}
|
|
1050
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time')} # Open the interactive dashboard
|
|
1051
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time github create')} # Create a GitHub repository
|
|
1052
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time -r <repo-url> -d "-2d" -y')} # Push with a commit date two days ago
|
|
1053
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time fix')} # Scan for and remove staged secrets
|
|
1054
|
+
${chalk.hex("#34D399")("$")} ${chalk.white('git-push-time pull')} # Pull the latest changes
|
|
1055
|
+
`,
|
|
1056
|
+
);
|
|
1057
|
+
}
|