@doccov/cli 0.25.7 → 0.25.9

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.
Files changed (3) hide show
  1. package/README.md +91 -20
  2. package/dist/cli.js +1199 -270
  3. package/package.json +3 -2
package/dist/cli.js CHANGED
@@ -151,6 +151,995 @@ import * as path10 from "node:path";
151
151
  import { fileURLToPath } from "node:url";
152
152
  import { Command } from "commander";
153
153
 
154
+ // ../cli-utils/dist/index.js
155
+ import chalk from "chalk";
156
+ import chalk2 from "chalk";
157
+ var colors = {
158
+ success: chalk.green,
159
+ error: chalk.red,
160
+ warning: chalk.yellow,
161
+ info: chalk.cyan,
162
+ muted: chalk.gray,
163
+ bold: chalk.bold,
164
+ dim: chalk.dim,
165
+ underline: chalk.underline,
166
+ primary: chalk.cyan,
167
+ secondary: chalk.magenta,
168
+ path: chalk.cyan,
169
+ number: chalk.yellow,
170
+ code: chalk.gray
171
+ };
172
+ var symbols = {
173
+ success: "✓",
174
+ error: "✗",
175
+ warning: "⚠",
176
+ info: "ℹ",
177
+ spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
178
+ bullet: "•",
179
+ arrow: "→",
180
+ arrowRight: "›",
181
+ line: "─",
182
+ corner: "└",
183
+ vertical: "│",
184
+ horizontalLine: "─"
185
+ };
186
+ var asciiSymbols = {
187
+ success: "+",
188
+ error: "x",
189
+ warning: "!",
190
+ info: "i",
191
+ spinner: ["-", "\\", "|", "/"],
192
+ bullet: "*",
193
+ arrow: "->",
194
+ arrowRight: ">",
195
+ line: "-",
196
+ corner: "\\",
197
+ vertical: "|",
198
+ horizontalLine: "-"
199
+ };
200
+ function getSymbols(unicodeSupport = true) {
201
+ return unicodeSupport ? symbols : asciiSymbols;
202
+ }
203
+ var prefix = {
204
+ success: colors.success(symbols.success),
205
+ error: colors.error(symbols.error),
206
+ warning: colors.warning(symbols.warning),
207
+ info: colors.info(symbols.info)
208
+ };
209
+ function isTTY() {
210
+ return Boolean(process.stdout.isTTY);
211
+ }
212
+ function isCI() {
213
+ return Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS || process.env.JENKINS_URL || process.env.BUILDKITE || process.env.TEAMCITY_VERSION || process.env.TF_BUILD || process.env.CODEBUILD_BUILD_ID || process.env.BITBUCKET_BUILD_NUMBER);
214
+ }
215
+ function isInteractive() {
216
+ return isTTY() && !isCI();
217
+ }
218
+ function supportsUnicode() {
219
+ if (process.platform === "win32") {
220
+ return Boolean(process.env.WT_SESSION) || process.env.TERM_PROGRAM === "vscode";
221
+ }
222
+ return process.env.TERM !== "linux";
223
+ }
224
+ var MIN_TERMINAL_WIDTH = 40;
225
+ var DEFAULT_TERMINAL_WIDTH = 80;
226
+ function getTerminalWidth() {
227
+ const width = process.stdout.columns || DEFAULT_TERMINAL_WIDTH;
228
+ return Math.max(width, MIN_TERMINAL_WIDTH);
229
+ }
230
+ function formatDuration(ms) {
231
+ if (ms < 1000) {
232
+ return `${ms}ms`;
233
+ }
234
+ const seconds = ms / 1000;
235
+ if (seconds < 60) {
236
+ return `${seconds.toFixed(1)}s`;
237
+ }
238
+ const minutes = Math.floor(seconds / 60);
239
+ const remainingSeconds = Math.floor(seconds % 60);
240
+ return `${minutes}m ${remainingSeconds}s`;
241
+ }
242
+ var cursor = {
243
+ hide: "\x1B[?25l",
244
+ show: "\x1B[?25h",
245
+ up: (n = 1) => `\x1B[${n}A`,
246
+ down: (n = 1) => `\x1B[${n}B`,
247
+ forward: (n = 1) => `\x1B[${n}C`,
248
+ back: (n = 1) => `\x1B[${n}D`,
249
+ left: "\x1B[G",
250
+ clearLine: "\x1B[2K",
251
+ clearDown: "\x1B[J",
252
+ save: "\x1B[s",
253
+ restore: "\x1B[u"
254
+ };
255
+ function clearLine() {
256
+ if (isTTY()) {
257
+ process.stdout.write(cursor.clearLine + cursor.left);
258
+ }
259
+ }
260
+ function moveCursorUp(lines = 1) {
261
+ if (isTTY()) {
262
+ process.stdout.write(cursor.up(lines));
263
+ }
264
+ }
265
+ function hideCursor() {
266
+ if (isTTY()) {
267
+ process.stdout.write(cursor.hide);
268
+ }
269
+ }
270
+ function showCursor() {
271
+ if (isTTY()) {
272
+ process.stdout.write(cursor.show);
273
+ }
274
+ }
275
+ function truncate(text, maxLength) {
276
+ if (text.length <= maxLength)
277
+ return text;
278
+ return `${text.slice(0, maxLength - 1)}…`;
279
+ }
280
+ var ANSI_REGEX = /\x1B\[[0-9;]*[a-zA-Z]/g;
281
+ function stripAnsi(text) {
282
+ return text.replace(ANSI_REGEX, "");
283
+ }
284
+
285
+ class MultiProgress {
286
+ bars = new Map;
287
+ barOrder = [];
288
+ options;
289
+ spinnerFrames;
290
+ spinnerIndex = 0;
291
+ timer = null;
292
+ lastRenderedLines = 0;
293
+ symbols = getSymbols(supportsUnicode());
294
+ sigintHandler = null;
295
+ filledChar;
296
+ emptyChar;
297
+ constructor(options = {}) {
298
+ this.options = {
299
+ barWidth: options.barWidth,
300
+ showPercent: options.showPercent ?? true,
301
+ showCount: options.showCount ?? true,
302
+ spinnerInterval: options.spinnerInterval ?? 80
303
+ };
304
+ this.spinnerFrames = supportsUnicode() ? ["◐", "◓", "◑", "◒"] : ["-", "\\", "|", "/"];
305
+ const unicode = supportsUnicode();
306
+ this.filledChar = unicode ? "█" : "#";
307
+ this.emptyChar = unicode ? "░" : "-";
308
+ }
309
+ start() {
310
+ if (!isInteractive())
311
+ return this;
312
+ hideCursor();
313
+ this.setupSignalHandler();
314
+ this.timer = setInterval(() => {
315
+ this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;
316
+ if (this.bars.size > 0 && [...this.bars.values()].some((b) => b.status === "active")) {
317
+ this.render();
318
+ }
319
+ }, this.options.spinnerInterval);
320
+ return this;
321
+ }
322
+ add(config) {
323
+ const state = {
324
+ id: config.id,
325
+ label: config.label,
326
+ total: config.total ?? 100,
327
+ current: config.current ?? 0,
328
+ status: "active",
329
+ startTime: Date.now()
330
+ };
331
+ this.bars.set(config.id, state);
332
+ if (!this.barOrder.includes(config.id)) {
333
+ this.barOrder.push(config.id);
334
+ }
335
+ if (!isInteractive()) {
336
+ console.log(`${this.symbols.bullet} ${config.label}`);
337
+ } else {
338
+ this.render();
339
+ }
340
+ return this;
341
+ }
342
+ update(id, current, label) {
343
+ const bar = this.bars.get(id);
344
+ if (!bar)
345
+ return this;
346
+ bar.current = Math.min(current, bar.total);
347
+ if (label !== undefined)
348
+ bar.label = label;
349
+ if (isInteractive()) {
350
+ this.render();
351
+ }
352
+ return this;
353
+ }
354
+ increment(id, amount = 1) {
355
+ const bar = this.bars.get(id);
356
+ if (!bar)
357
+ return this;
358
+ return this.update(id, bar.current + amount);
359
+ }
360
+ complete(id, label) {
361
+ const bar = this.bars.get(id);
362
+ if (!bar)
363
+ return this;
364
+ bar.status = "completed";
365
+ bar.current = bar.total;
366
+ if (label !== undefined)
367
+ bar.label = label;
368
+ if (!isInteractive()) {
369
+ console.log(`${colors.success(this.symbols.success)} ${bar.label}`);
370
+ } else {
371
+ this.render();
372
+ }
373
+ return this;
374
+ }
375
+ fail(id, label) {
376
+ const bar = this.bars.get(id);
377
+ if (!bar)
378
+ return this;
379
+ bar.status = "failed";
380
+ if (label !== undefined)
381
+ bar.label = label;
382
+ if (!isInteractive()) {
383
+ console.log(`${colors.error(this.symbols.error)} ${bar.label}`);
384
+ } else {
385
+ this.render();
386
+ }
387
+ return this;
388
+ }
389
+ remove(id) {
390
+ this.bars.delete(id);
391
+ this.barOrder = this.barOrder.filter((i) => i !== id);
392
+ if (isInteractive()) {
393
+ this.render();
394
+ }
395
+ return this;
396
+ }
397
+ get(id) {
398
+ return this.bars.get(id);
399
+ }
400
+ get allDone() {
401
+ if (this.bars.size === 0)
402
+ return true;
403
+ return [...this.bars.values()].every((b) => b.status !== "active");
404
+ }
405
+ stop() {
406
+ if (this.timer) {
407
+ clearInterval(this.timer);
408
+ this.timer = null;
409
+ }
410
+ this.cleanup();
411
+ return this;
412
+ }
413
+ render() {
414
+ if (!isTTY())
415
+ return;
416
+ this.clearOutput();
417
+ const width = getTerminalWidth();
418
+ const lines = [];
419
+ for (const id of this.barOrder) {
420
+ const bar = this.bars.get(id);
421
+ if (!bar)
422
+ continue;
423
+ const line = this.renderBar(bar, width);
424
+ lines.push(line);
425
+ }
426
+ if (lines.length > 0) {
427
+ process.stdout.write(lines.join(`
428
+ `));
429
+ this.lastRenderedLines = lines.length;
430
+ }
431
+ }
432
+ renderBar(bar, termWidth) {
433
+ const parts = [];
434
+ let symbol;
435
+ switch (bar.status) {
436
+ case "completed":
437
+ symbol = colors.success(this.symbols.success);
438
+ break;
439
+ case "failed":
440
+ symbol = colors.error(this.symbols.error);
441
+ break;
442
+ default:
443
+ symbol = colors.primary(this.spinnerFrames[this.spinnerIndex]);
444
+ }
445
+ parts.push(symbol);
446
+ parts.push(bar.label);
447
+ const suffixParts = [];
448
+ if (this.options.showPercent) {
449
+ const pct = bar.total === 0 ? 0 : Math.round(bar.current / bar.total * 100);
450
+ suffixParts.push(`${pct}%`);
451
+ }
452
+ if (this.options.showCount) {
453
+ suffixParts.push(`${bar.current}/${bar.total}`);
454
+ }
455
+ const suffix = suffixParts.length > 0 ? ` ${suffixParts.join(" ")}` : "";
456
+ const labelLen = stripAnsi(parts.join(" ")).length + 1;
457
+ const bracketLen = 2;
458
+ const suffixLen = stripAnsi(suffix).length;
459
+ const minBarWidth = 10;
460
+ const availableWidth = termWidth - labelLen - bracketLen - suffixLen - 1;
461
+ const barWidth = this.options.barWidth ?? Math.max(minBarWidth, Math.min(30, availableWidth));
462
+ const filledWidth = Math.round(bar.current / bar.total * barWidth);
463
+ const emptyWidth = barWidth - filledWidth;
464
+ const barViz = `[${this.filledChar.repeat(filledWidth)}${this.emptyChar.repeat(emptyWidth)}]`;
465
+ parts.push(barViz);
466
+ return truncate(parts.join(" ") + suffix, termWidth);
467
+ }
468
+ clearOutput() {
469
+ if (!isTTY() || this.lastRenderedLines === 0)
470
+ return;
471
+ for (let i = 0;i < this.lastRenderedLines; i++) {
472
+ if (i > 0)
473
+ process.stdout.write(cursor.up(1));
474
+ clearLine();
475
+ }
476
+ this.lastRenderedLines = 0;
477
+ }
478
+ setupSignalHandler() {
479
+ this.sigintHandler = () => {
480
+ this.cleanup();
481
+ process.exit(130);
482
+ };
483
+ process.on("SIGINT", this.sigintHandler);
484
+ }
485
+ cleanup() {
486
+ if (this.sigintHandler) {
487
+ process.removeListener("SIGINT", this.sigintHandler);
488
+ this.sigintHandler = null;
489
+ }
490
+ showCursor();
491
+ if (isTTY() && this.lastRenderedLines > 0) {
492
+ process.stdout.write(`
493
+ `);
494
+ }
495
+ }
496
+ }
497
+ class ProgressBar {
498
+ total;
499
+ current;
500
+ label;
501
+ width;
502
+ showPercent;
503
+ showCount;
504
+ showETA;
505
+ filledChar;
506
+ emptyChar;
507
+ startTime = null;
508
+ lastRender = "";
509
+ symbols = getSymbols(supportsUnicode());
510
+ sigintHandler = null;
511
+ isComplete = false;
512
+ constructor(options = {}) {
513
+ this.total = options.total ?? 100;
514
+ this.current = options.current ?? 0;
515
+ this.label = options.label ?? "";
516
+ this.width = options.width;
517
+ this.showPercent = options.showPercent ?? true;
518
+ this.showCount = options.showCount ?? true;
519
+ this.showETA = options.showETA ?? true;
520
+ const unicode = supportsUnicode();
521
+ this.filledChar = options.chars?.filled ?? (unicode ? "█" : "#");
522
+ this.emptyChar = options.chars?.empty ?? (unicode ? "░" : "-");
523
+ }
524
+ start(label) {
525
+ if (label !== undefined)
526
+ this.label = label;
527
+ this.startTime = Date.now();
528
+ this.current = 0;
529
+ this.isComplete = false;
530
+ if (!isInteractive()) {
531
+ console.log(`${this.symbols.bullet} ${this.label}`);
532
+ return this;
533
+ }
534
+ hideCursor();
535
+ this.setupSignalHandler();
536
+ this.render();
537
+ return this;
538
+ }
539
+ update(current) {
540
+ this.current = Math.min(current, this.total);
541
+ if (isInteractive()) {
542
+ this.render();
543
+ }
544
+ return this;
545
+ }
546
+ increment(amount = 1) {
547
+ return this.update(this.current + amount);
548
+ }
549
+ setLabel(label) {
550
+ this.label = label;
551
+ if (isInteractive()) {
552
+ this.render();
553
+ }
554
+ return this;
555
+ }
556
+ setTotal(total) {
557
+ this.total = total;
558
+ if (isInteractive()) {
559
+ this.render();
560
+ }
561
+ return this;
562
+ }
563
+ complete(label) {
564
+ if (label !== undefined)
565
+ this.label = label;
566
+ this.current = this.total;
567
+ this.isComplete = true;
568
+ if (!isInteractive()) {
569
+ console.log(`${colors.success(this.symbols.success)} ${this.label}`);
570
+ } else {
571
+ clearLine();
572
+ process.stdout.write(`${colors.success(this.symbols.success)} ${this.label}
573
+ `);
574
+ }
575
+ this.cleanup();
576
+ return this;
577
+ }
578
+ fail(label) {
579
+ if (label !== undefined)
580
+ this.label = label;
581
+ this.isComplete = true;
582
+ if (!isInteractive()) {
583
+ console.log(`${colors.error(this.symbols.error)} ${this.label}`);
584
+ } else {
585
+ clearLine();
586
+ process.stdout.write(`${colors.error(this.symbols.error)} ${this.label}
587
+ `);
588
+ }
589
+ this.cleanup();
590
+ return this;
591
+ }
592
+ get percentage() {
593
+ return this.total === 0 ? 0 : Math.round(this.current / this.total * 100);
594
+ }
595
+ get isDone() {
596
+ return this.isComplete || this.current >= this.total;
597
+ }
598
+ render() {
599
+ if (!isTTY())
600
+ return;
601
+ const termWidth = getTerminalWidth();
602
+ const parts = [];
603
+ if (this.label) {
604
+ parts.push(this.label);
605
+ }
606
+ const suffixParts = [];
607
+ if (this.showPercent) {
608
+ suffixParts.push(`${this.percentage}%`);
609
+ }
610
+ if (this.showCount) {
611
+ suffixParts.push(`${this.current}/${this.total}`);
612
+ }
613
+ if (this.showETA && this.startTime) {
614
+ const eta = this.calculateETA();
615
+ if (eta)
616
+ suffixParts.push(eta);
617
+ }
618
+ const suffix = suffixParts.length > 0 ? ` ${suffixParts.join(" ")}` : "";
619
+ const labelLen = this.label ? stripAnsi(this.label).length + 1 : 0;
620
+ const bracketLen = 2;
621
+ const suffixLen = stripAnsi(suffix).length;
622
+ const minBarWidth = 10;
623
+ const availableWidth = termWidth - labelLen - bracketLen - suffixLen - 1;
624
+ const barWidth = this.width ?? Math.max(minBarWidth, Math.min(40, availableWidth));
625
+ const filledWidth = Math.round(this.current / this.total * barWidth);
626
+ const emptyWidth = barWidth - filledWidth;
627
+ const bar = `[${this.filledChar.repeat(filledWidth)}${this.emptyChar.repeat(emptyWidth)}]`;
628
+ parts.push(bar);
629
+ const line = truncate(parts.join(" ") + suffix, termWidth);
630
+ if (line !== this.lastRender) {
631
+ clearLine();
632
+ process.stdout.write(line);
633
+ this.lastRender = line;
634
+ }
635
+ }
636
+ calculateETA() {
637
+ if (!this.startTime || this.current === 0)
638
+ return null;
639
+ const elapsed = Date.now() - this.startTime;
640
+ if (elapsed < 1000)
641
+ return null;
642
+ const rate = this.current / elapsed;
643
+ const remaining = this.total - this.current;
644
+ const etaMs = remaining / rate;
645
+ return `ETA ${formatDuration(etaMs)}`;
646
+ }
647
+ setupSignalHandler() {
648
+ this.sigintHandler = () => {
649
+ this.cleanup();
650
+ process.exit(130);
651
+ };
652
+ process.on("SIGINT", this.sigintHandler);
653
+ }
654
+ cleanup() {
655
+ if (this.sigintHandler) {
656
+ process.removeListener("SIGINT", this.sigintHandler);
657
+ this.sigintHandler = null;
658
+ }
659
+ showCursor();
660
+ }
661
+ }
662
+ var spinnerColors = {
663
+ cyan: chalk2.cyan,
664
+ yellow: chalk2.yellow,
665
+ green: chalk2.green,
666
+ red: chalk2.red,
667
+ magenta: chalk2.magenta,
668
+ blue: chalk2.blue,
669
+ white: chalk2.white
670
+ };
671
+ var FRAME_SETS = {
672
+ dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
673
+ circle: ["◐", "◓", "◑", "◒"]
674
+ };
675
+ var ASCII_FRAME_SET = ["-", "\\", "|", "/"];
676
+
677
+ class Spinner {
678
+ label;
679
+ detail;
680
+ frames;
681
+ interval;
682
+ colorFn;
683
+ frameIndex = 0;
684
+ timer = null;
685
+ state = "stopped";
686
+ symbols = getSymbols(supportsUnicode());
687
+ lastRenderedLines = 0;
688
+ sigintHandler = null;
689
+ constructor(options = {}) {
690
+ this.label = options.label ?? "";
691
+ this.detail = options.detail;
692
+ this.interval = options.interval ?? 80;
693
+ this.colorFn = spinnerColors[options.color ?? "cyan"];
694
+ const style = options.style ?? "circle";
695
+ this.frames = supportsUnicode() ? FRAME_SETS[style] : ASCII_FRAME_SET;
696
+ }
697
+ start(label) {
698
+ if (label !== undefined)
699
+ this.label = label;
700
+ if (this.state === "spinning")
701
+ return this;
702
+ this.state = "spinning";
703
+ this.frameIndex = 0;
704
+ this.lastRenderedLines = 0;
705
+ if (!isInteractive()) {
706
+ console.log(`${this.symbols.bullet} ${this.label}`);
707
+ return this;
708
+ }
709
+ hideCursor();
710
+ this.setupSignalHandler();
711
+ this.render();
712
+ this.timer = setInterval(() => {
713
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
714
+ this.render();
715
+ }, this.interval);
716
+ return this;
717
+ }
718
+ stop() {
719
+ if (this.timer) {
720
+ clearInterval(this.timer);
721
+ this.timer = null;
722
+ }
723
+ this.state = "stopped";
724
+ this.clearOutput();
725
+ this.cleanup();
726
+ return this;
727
+ }
728
+ success(label) {
729
+ if (label !== undefined)
730
+ this.label = label;
731
+ this.finish("success");
732
+ return this;
733
+ }
734
+ fail(label) {
735
+ if (label !== undefined)
736
+ this.label = label;
737
+ this.finish("error");
738
+ return this;
739
+ }
740
+ update(label) {
741
+ this.label = label;
742
+ if (this.state === "spinning" && isInteractive()) {
743
+ this.render();
744
+ }
745
+ return this;
746
+ }
747
+ setDetail(detail) {
748
+ this.detail = detail;
749
+ if (this.state === "spinning" && isInteractive()) {
750
+ this.render();
751
+ }
752
+ return this;
753
+ }
754
+ get isSpinning() {
755
+ return this.state === "spinning";
756
+ }
757
+ finish(state) {
758
+ if (this.timer) {
759
+ clearInterval(this.timer);
760
+ this.timer = null;
761
+ }
762
+ this.state = state;
763
+ if (!isInteractive()) {
764
+ const symbol = state === "success" ? this.symbols.success : this.symbols.error;
765
+ const colorFn = state === "success" ? colors.success : colors.error;
766
+ console.log(`${colorFn(symbol)} ${this.label}`);
767
+ } else {
768
+ this.clearOutput();
769
+ const symbol = state === "success" ? this.symbols.success : this.symbols.error;
770
+ const colorFn = state === "success" ? colors.success : colors.error;
771
+ process.stdout.write(`${colorFn(symbol)} ${this.label}
772
+ `);
773
+ }
774
+ this.cleanup();
775
+ }
776
+ render() {
777
+ if (!isTTY())
778
+ return;
779
+ this.clearOutput();
780
+ const frame = this.colorFn(this.frames[this.frameIndex]);
781
+ const width = getTerminalWidth();
782
+ const mainLine = truncate(`${frame} ${this.label}`, width);
783
+ process.stdout.write(mainLine);
784
+ let lines = 1;
785
+ if (this.detail) {
786
+ const detailLine = truncate(` ${colors.muted(this.detail)}`, width);
787
+ process.stdout.write(`
788
+ ${detailLine}`);
789
+ lines = 2;
790
+ }
791
+ this.lastRenderedLines = lines;
792
+ }
793
+ clearOutput() {
794
+ if (!isTTY())
795
+ return;
796
+ for (let i = 0;i < this.lastRenderedLines; i++) {
797
+ if (i > 0)
798
+ process.stdout.write(cursor.up(1));
799
+ clearLine();
800
+ }
801
+ }
802
+ setupSignalHandler() {
803
+ this.sigintHandler = () => {
804
+ this.cleanup();
805
+ process.exit(130);
806
+ };
807
+ process.on("SIGINT", this.sigintHandler);
808
+ }
809
+ cleanup() {
810
+ if (this.sigintHandler) {
811
+ process.removeListener("SIGINT", this.sigintHandler);
812
+ this.sigintHandler = null;
813
+ }
814
+ showCursor();
815
+ }
816
+ }
817
+ function spinner(label, options) {
818
+ return new Spinner({ ...options, label }).start();
819
+ }
820
+
821
+ class StepProgress {
822
+ steps = [];
823
+ showNumbers;
824
+ spinnerInterval;
825
+ spinnerFrames;
826
+ spinnerIndex = 0;
827
+ timer = null;
828
+ symbols = getSymbols(supportsUnicode());
829
+ lastRenderedLines = 0;
830
+ sigintHandler = null;
831
+ constructor(options = {}) {
832
+ this.showNumbers = options.showNumbers ?? true;
833
+ this.spinnerInterval = options.spinnerInterval ?? 80;
834
+ this.spinnerFrames = supportsUnicode() ? ["◐", "◓", "◑", "◒"] : ["-", "\\", "|", "/"];
835
+ if (options.steps) {
836
+ this.steps = options.steps.map((label) => ({ label, status: "pending" }));
837
+ }
838
+ }
839
+ start() {
840
+ if (!isInteractive())
841
+ return this;
842
+ hideCursor();
843
+ this.setupSignalHandler();
844
+ this.render();
845
+ this.timer = setInterval(() => {
846
+ this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;
847
+ if (this.steps.some((s) => s.status === "active")) {
848
+ this.render();
849
+ }
850
+ }, this.spinnerInterval);
851
+ return this;
852
+ }
853
+ addStep(label) {
854
+ this.steps.push({ label, status: "pending" });
855
+ if (isInteractive())
856
+ this.render();
857
+ return this;
858
+ }
859
+ startStep(index) {
860
+ if (index >= 0 && index < this.steps.length) {
861
+ this.steps[index].status = "active";
862
+ this.steps[index].startTime = Date.now();
863
+ if (isInteractive()) {
864
+ this.render();
865
+ } else {
866
+ const step = this.steps[index];
867
+ const prefix2 = this.showNumbers ? `[${index + 1}/${this.steps.length}] ` : "";
868
+ console.log(`${this.symbols.bullet} ${prefix2}${step.label}...`);
869
+ }
870
+ }
871
+ return this;
872
+ }
873
+ completeStep(index) {
874
+ if (index >= 0 && index < this.steps.length) {
875
+ this.steps[index].status = "completed";
876
+ this.steps[index].endTime = Date.now();
877
+ if (isInteractive()) {
878
+ this.render();
879
+ } else {
880
+ const step = this.steps[index];
881
+ const duration = this.getStepDuration(step);
882
+ const prefix2 = this.showNumbers ? `[${index + 1}/${this.steps.length}] ` : "";
883
+ console.log(`${colors.success(this.symbols.success)} ${prefix2}${step.label}${duration}`);
884
+ }
885
+ }
886
+ return this;
887
+ }
888
+ failStep(index) {
889
+ if (index >= 0 && index < this.steps.length) {
890
+ this.steps[index].status = "failed";
891
+ this.steps[index].endTime = Date.now();
892
+ if (isInteractive()) {
893
+ this.render();
894
+ } else {
895
+ const step = this.steps[index];
896
+ const prefix2 = this.showNumbers ? `[${index + 1}/${this.steps.length}] ` : "";
897
+ console.log(`${colors.error(this.symbols.error)} ${prefix2}${step.label}`);
898
+ }
899
+ }
900
+ return this;
901
+ }
902
+ skipStep(index) {
903
+ if (index >= 0 && index < this.steps.length) {
904
+ this.steps[index].status = "skipped";
905
+ if (isInteractive())
906
+ this.render();
907
+ }
908
+ return this;
909
+ }
910
+ async run(tasks) {
911
+ this.steps = tasks.map((t) => ({ label: t.label, status: "pending" }));
912
+ this.start();
913
+ const results = [];
914
+ let failed = false;
915
+ for (let i = 0;i < tasks.length; i++) {
916
+ if (failed) {
917
+ this.skipStep(i);
918
+ continue;
919
+ }
920
+ this.startStep(i);
921
+ try {
922
+ results.push(await tasks[i].task());
923
+ this.completeStep(i);
924
+ } catch {
925
+ this.failStep(i);
926
+ failed = true;
927
+ }
928
+ }
929
+ this.stop();
930
+ return { results, failed };
931
+ }
932
+ stop() {
933
+ if (this.timer) {
934
+ clearInterval(this.timer);
935
+ this.timer = null;
936
+ }
937
+ this.cleanup();
938
+ return this;
939
+ }
940
+ get currentStepIndex() {
941
+ const activeIdx = this.steps.findIndex((s) => s.status === "active");
942
+ if (activeIdx >= 0)
943
+ return activeIdx;
944
+ return this.steps.findIndex((s) => s.status === "pending");
945
+ }
946
+ render() {
947
+ if (!isTTY())
948
+ return;
949
+ if (this.lastRenderedLines > 0) {
950
+ moveCursorUp(this.lastRenderedLines - 1);
951
+ for (let i = 0;i < this.lastRenderedLines; i++) {
952
+ clearLine();
953
+ if (i < this.lastRenderedLines - 1) {
954
+ process.stdout.write(cursor.down(1));
955
+ }
956
+ }
957
+ moveCursorUp(this.lastRenderedLines - 1);
958
+ }
959
+ const width = getTerminalWidth();
960
+ const lines = [];
961
+ for (let i = 0;i < this.steps.length; i++) {
962
+ const step = this.steps[i];
963
+ const prefix2 = this.showNumbers ? `[${i + 1}/${this.steps.length}] ` : "";
964
+ const duration = this.getStepDuration(step);
965
+ let symbol;
966
+ let text;
967
+ switch (step.status) {
968
+ case "completed":
969
+ symbol = colors.success(this.symbols.success);
970
+ text = `${prefix2}${step.label}${duration}`;
971
+ break;
972
+ case "failed":
973
+ symbol = colors.error(this.symbols.error);
974
+ text = `${prefix2}${step.label}`;
975
+ break;
976
+ case "active":
977
+ symbol = colors.primary(this.spinnerFrames[this.spinnerIndex]);
978
+ text = `${prefix2}${step.label}`;
979
+ break;
980
+ case "skipped":
981
+ symbol = colors.muted(this.symbols.bullet);
982
+ text = colors.muted(`${prefix2}${step.label} (skipped)`);
983
+ break;
984
+ default:
985
+ symbol = colors.muted("○");
986
+ text = colors.muted(`${prefix2}${step.label}`);
987
+ }
988
+ lines.push(truncate(`${symbol} ${text}`, width));
989
+ }
990
+ process.stdout.write(lines.join(`
991
+ `));
992
+ this.lastRenderedLines = lines.length;
993
+ }
994
+ getStepDuration(step) {
995
+ if (step.startTime && step.endTime) {
996
+ const ms = step.endTime - step.startTime;
997
+ return colors.muted(` (${formatDuration(ms)})`);
998
+ }
999
+ return "";
1000
+ }
1001
+ setupSignalHandler() {
1002
+ this.sigintHandler = () => {
1003
+ this.cleanup();
1004
+ process.exit(130);
1005
+ };
1006
+ process.on("SIGINT", this.sigintHandler);
1007
+ }
1008
+ cleanup() {
1009
+ if (this.sigintHandler) {
1010
+ process.removeListener("SIGINT", this.sigintHandler);
1011
+ this.sigintHandler = null;
1012
+ }
1013
+ showCursor();
1014
+ if (isTTY() && this.lastRenderedLines > 0) {
1015
+ process.stdout.write(`
1016
+ `);
1017
+ }
1018
+ }
1019
+ }
1020
+ class Summary {
1021
+ items = [];
1022
+ title;
1023
+ boxed;
1024
+ keyWidth;
1025
+ symbols = getSymbols(supportsUnicode());
1026
+ constructor(options = {}) {
1027
+ this.title = options.title;
1028
+ this.boxed = options.boxed ?? false;
1029
+ this.keyWidth = options.keyWidth;
1030
+ }
1031
+ add(item) {
1032
+ this.items.push(item);
1033
+ return this;
1034
+ }
1035
+ addKeyValue(key, value, status) {
1036
+ return this.add({ key, value, status });
1037
+ }
1038
+ addWithThreshold(key, value, threshold) {
1039
+ const status = this.evaluateThreshold(value, threshold) ? "pass" : "fail";
1040
+ return this.add({ key, value, status, threshold });
1041
+ }
1042
+ print() {
1043
+ const output = this.render();
1044
+ console.log(output);
1045
+ }
1046
+ render() {
1047
+ if (this.items.length === 0)
1048
+ return "";
1049
+ const lines = [];
1050
+ const termWidth = getTerminalWidth();
1051
+ const calculatedKeyWidth = this.keyWidth ?? Math.max(...this.items.map((i) => i.key.length));
1052
+ if (this.boxed) {
1053
+ return this.renderBoxed(calculatedKeyWidth, termWidth);
1054
+ }
1055
+ if (this.title) {
1056
+ lines.push(colors.bold(this.title));
1057
+ lines.push("");
1058
+ }
1059
+ for (const item of this.items) {
1060
+ lines.push(this.formatItem(item, calculatedKeyWidth));
1061
+ }
1062
+ return lines.join(`
1063
+ `);
1064
+ }
1065
+ renderBoxed(keyWidth, termWidth) {
1066
+ const lines = [];
1067
+ const unicode = supportsUnicode();
1068
+ const box = unicode ? { tl: "┌", tr: "┐", bl: "└", br: "┘", h: "─", v: "│" } : { tl: "+", tr: "+", bl: "+", br: "+", h: "-", v: "|" };
1069
+ const contentLines = this.items.map((item) => this.formatItem(item, keyWidth));
1070
+ const maxContentWidth = Math.max(...contentLines.map((l) => stripAnsi(l).length), this.title ? this.title.length : 0);
1071
+ const innerWidth = Math.min(maxContentWidth + 2, termWidth - 4);
1072
+ lines.push(box.tl + box.h.repeat(innerWidth) + box.tr);
1073
+ if (this.title) {
1074
+ const padding = innerWidth - this.title.length;
1075
+ const leftPad = Math.floor(padding / 2);
1076
+ const rightPad = padding - leftPad;
1077
+ lines.push(`${box.v}${" ".repeat(leftPad)}${colors.bold(this.title)}${" ".repeat(rightPad)}${box.v}`);
1078
+ lines.push(`${box.v}${" ".repeat(innerWidth)}${box.v}`);
1079
+ }
1080
+ for (const line of contentLines) {
1081
+ const visibleLen = stripAnsi(line).length;
1082
+ const padding = innerWidth - visibleLen - 1;
1083
+ lines.push(`${box.v} ${line}${" ".repeat(Math.max(0, padding))}${box.v}`);
1084
+ }
1085
+ lines.push(box.bl + box.h.repeat(innerWidth) + box.br);
1086
+ return lines.join(`
1087
+ `);
1088
+ }
1089
+ formatItem(item, keyWidth) {
1090
+ const key = item.key.padEnd(keyWidth);
1091
+ const value = String(item.value);
1092
+ let indicator = "";
1093
+ if (item.status) {
1094
+ switch (item.status) {
1095
+ case "pass":
1096
+ indicator = `${colors.success(this.symbols.success)} `;
1097
+ break;
1098
+ case "fail":
1099
+ indicator = `${colors.error(this.symbols.error)} `;
1100
+ break;
1101
+ case "warn":
1102
+ indicator = `${colors.warning(this.symbols.warning)} `;
1103
+ break;
1104
+ case "info":
1105
+ indicator = `${colors.info(this.symbols.info)} `;
1106
+ break;
1107
+ }
1108
+ }
1109
+ let thresholdStr = "";
1110
+ if (item.threshold) {
1111
+ const { operator, value: thresh } = item.threshold;
1112
+ thresholdStr = colors.muted(` (${operator} ${thresh})`);
1113
+ }
1114
+ let coloredValue = value;
1115
+ if (item.status === "pass")
1116
+ coloredValue = colors.success(value);
1117
+ else if (item.status === "fail")
1118
+ coloredValue = colors.error(value);
1119
+ else if (item.status === "warn")
1120
+ coloredValue = colors.warning(value);
1121
+ return `${indicator}${colors.muted(key)} ${coloredValue}${thresholdStr}`;
1122
+ }
1123
+ evaluateThreshold(value, threshold) {
1124
+ if (!threshold)
1125
+ return true;
1126
+ const { operator, value: thresh } = threshold;
1127
+ switch (operator) {
1128
+ case "<":
1129
+ return value < thresh;
1130
+ case ">":
1131
+ return value > thresh;
1132
+ case "<=":
1133
+ return value <= thresh;
1134
+ case ">=":
1135
+ return value >= thresh;
1136
+ }
1137
+ }
1138
+ }
1139
+ function summary(options) {
1140
+ return new Summary(options);
1141
+ }
1142
+
154
1143
  // src/commands/check/index.ts
155
1144
  import {
156
1145
  buildDocCovSpec,
@@ -159,11 +1148,11 @@ import {
159
1148
  parseExamplesFlag,
160
1149
  resolveTarget
161
1150
  } from "@doccov/sdk";
162
- import chalk6 from "chalk";
1151
+ import chalk7 from "chalk";
163
1152
 
164
1153
  // src/utils/filter-options.ts
165
1154
  import { mergeFilters, parseListFlag } from "@doccov/sdk";
166
- import chalk from "chalk";
1155
+ import chalk3 from "chalk";
167
1156
  var parseVisibilityFlag = (value) => {
168
1157
  if (!value)
169
1158
  return;
@@ -180,7 +1169,7 @@ var parseVisibilityFlag = (value) => {
180
1169
  }
181
1170
  return result.length > 0 ? result : undefined;
182
1171
  };
183
- var formatList = (label, values) => `${label}: ${values.map((value) => chalk.cyan(value)).join(", ")}`;
1172
+ var formatList = (label, values) => `${label}: ${values.map((value) => chalk3.cyan(value)).join(", ")}`;
184
1173
  var mergeFilterOptions = (config, cliOptions) => {
185
1174
  const messages = [];
186
1175
  if (config?.include) {
@@ -212,57 +1201,6 @@ var mergeFilterOptions = (config, cliOptions) => {
212
1201
  };
213
1202
  };
214
1203
 
215
- // src/utils/progress.ts
216
- import chalk2 from "chalk";
217
- class StepProgress {
218
- steps;
219
- currentStep = 0;
220
- startTime;
221
- stepStartTime;
222
- constructor(steps) {
223
- this.steps = steps;
224
- this.startTime = Date.now();
225
- this.stepStartTime = Date.now();
226
- }
227
- start(stepIndex) {
228
- this.currentStep = stepIndex ?? 0;
229
- this.stepStartTime = Date.now();
230
- this.render();
231
- }
232
- next() {
233
- this.completeCurrentStep();
234
- this.currentStep++;
235
- if (this.currentStep < this.steps.length) {
236
- this.stepStartTime = Date.now();
237
- this.render();
238
- }
239
- }
240
- complete(message) {
241
- this.completeCurrentStep();
242
- if (message) {
243
- const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
244
- console.log(`${chalk2.green("✓")} ${message} ${chalk2.dim(`(${elapsed}s)`)}`);
245
- }
246
- }
247
- render() {
248
- const step = this.steps[this.currentStep];
249
- if (!step)
250
- return;
251
- const label = step.activeLabel ?? step.label;
252
- const prefix = chalk2.dim(`[${this.currentStep + 1}/${this.steps.length}]`);
253
- process.stdout.write(`\r${prefix} ${chalk2.cyan(label)}...`);
254
- }
255
- completeCurrentStep() {
256
- const step = this.steps[this.currentStep];
257
- if (!step)
258
- return;
259
- const elapsed = ((Date.now() - this.stepStartTime) / 1000).toFixed(1);
260
- const prefix = chalk2.dim(`[${this.currentStep + 1}/${this.steps.length}]`);
261
- process.stdout.write(`\r${" ".repeat(80)}\r`);
262
- console.log(`${prefix} ${step.label} ${chalk2.green("✓")} ${chalk2.dim(`(${elapsed}s)`)}`);
263
- }
264
- }
265
-
266
1204
  // src/utils/validation.ts
267
1205
  function clampPercentage(value, fallback = 80) {
268
1206
  if (Number.isNaN(value))
@@ -287,7 +1225,7 @@ import {
287
1225
  parseJSDocToPatch,
288
1226
  serializeJSDoc
289
1227
  } from "@doccov/sdk";
290
- import chalk3 from "chalk";
1228
+ import chalk5 from "chalk";
291
1229
 
292
1230
  // src/commands/check/utils.ts
293
1231
  import * as fs from "node:fs";
@@ -361,13 +1299,13 @@ async function handleFixes(openpkg, doccov, options, deps) {
361
1299
  }
362
1300
  const { fixable, nonFixable } = categorizeDrifts(allDrifts.map((d) => d.drift));
363
1301
  if (fixable.length === 0) {
364
- log(chalk3.yellow(`Found ${nonFixable.length} drift issue(s), but none are auto-fixable.`));
1302
+ log(chalk5.yellow(`Found ${nonFixable.length} drift issue(s), but none are auto-fixable.`));
365
1303
  return { fixedDriftKeys, editsApplied: 0, filesModified: 0 };
366
1304
  }
367
1305
  log("");
368
- log(chalk3.bold(`Found ${fixable.length} fixable issue(s)`));
1306
+ log(chalk5.bold(`Found ${fixable.length} fixable issue(s)`));
369
1307
  if (nonFixable.length > 0) {
370
- log(chalk3.gray(`(${nonFixable.length} non-fixable issue(s) skipped)`));
1308
+ log(chalk5.gray(`(${nonFixable.length} non-fixable issue(s) skipped)`));
371
1309
  }
372
1310
  log("");
373
1311
  const groupedDrifts = groupByExport(allDrifts.filter((d) => fixable.includes(d.drift)));
@@ -400,16 +1338,16 @@ async function handleFixes(openpkg, doccov, options, deps) {
400
1338
  const applyResult = await applyEdits(edits);
401
1339
  if (applyResult.errors.length > 0) {
402
1340
  for (const err of applyResult.errors) {
403
- error(chalk3.red(` ${err.file}: ${err.error}`));
1341
+ error(chalk5.red(` ${err.file}: ${err.error}`));
404
1342
  }
405
1343
  }
406
1344
  const totalFixes = Array.from(editsByFile.values()).reduce((sum, fileEdits) => sum + fileEdits.reduce((s, e) => s + e.fixes.length, 0), 0);
407
1345
  log("");
408
- log(chalk3.green(`✓ Applied ${totalFixes} fix(es) to ${applyResult.filesModified} file(s)`));
1346
+ log(chalk5.green(`✓ Applied ${totalFixes} fix(es) to ${applyResult.filesModified} file(s)`));
409
1347
  for (const [filePath, fileEdits] of editsByFile) {
410
1348
  const relativePath = path3.relative(targetDir, filePath);
411
1349
  const fixCount = fileEdits.reduce((s, e) => s + e.fixes.length, 0);
412
- log(chalk3.dim(` ${relativePath} (${fixCount} fixes)`));
1350
+ log(chalk5.dim(` ${relativePath} (${fixCount} fixes)`));
413
1351
  }
414
1352
  return {
415
1353
  fixedDriftKeys,
@@ -419,22 +1357,22 @@ async function handleFixes(openpkg, doccov, options, deps) {
419
1357
  }
420
1358
  function generateEditForExport(exp, drifts, targetDir, log) {
421
1359
  if (!exp.source?.file) {
422
- log(chalk3.gray(` Skipping ${exp.name}: no source location`));
1360
+ log(chalk5.gray(` Skipping ${exp.name}: no source location`));
423
1361
  return null;
424
1362
  }
425
1363
  if (exp.source.file.endsWith(".d.ts")) {
426
- log(chalk3.gray(` Skipping ${exp.name}: declaration file`));
1364
+ log(chalk5.gray(` Skipping ${exp.name}: declaration file`));
427
1365
  return null;
428
1366
  }
429
1367
  const filePath = path3.resolve(targetDir, exp.source.file);
430
1368
  if (!fs2.existsSync(filePath)) {
431
- log(chalk3.gray(` Skipping ${exp.name}: file not found`));
1369
+ log(chalk5.gray(` Skipping ${exp.name}: file not found`));
432
1370
  return null;
433
1371
  }
434
1372
  const sourceFile = createSourceFile(filePath);
435
1373
  const location = findJSDocLocation(sourceFile, exp.name, exp.source.line);
436
1374
  if (!location) {
437
- log(chalk3.gray(` Skipping ${exp.name}: could not find declaration`));
1375
+ log(chalk5.gray(` Skipping ${exp.name}: could not find declaration`));
438
1376
  return null;
439
1377
  }
440
1378
  let existingPatch = {};
@@ -459,13 +1397,13 @@ function generateEditForExport(exp, drifts, targetDir, log) {
459
1397
  return { filePath, edit, fixes, existingPatch };
460
1398
  }
461
1399
  function displayPreview(editsByFile, targetDir, log) {
462
- log(chalk3.bold("Preview - changes that would be made:"));
1400
+ log(chalk5.bold("Preview - changes that would be made:"));
463
1401
  log("");
464
1402
  for (const [filePath, fileEdits] of editsByFile) {
465
1403
  const relativePath = path3.relative(targetDir, filePath);
466
1404
  for (const { export: exp, edit, fixes } of fileEdits) {
467
- log(chalk3.cyan(`${relativePath}:${edit.startLine + 1}`));
468
- log(chalk3.bold(` ${exp.name}`));
1405
+ log(chalk5.cyan(`${relativePath}:${edit.startLine + 1}`));
1406
+ log(chalk5.bold(` ${exp.name}`));
469
1407
  log("");
470
1408
  if (edit.hasExisting && edit.existingJSDoc) {
471
1409
  const oldLines = edit.existingJSDoc.split(`
@@ -473,31 +1411,30 @@ function displayPreview(editsByFile, targetDir, log) {
473
1411
  const newLines = edit.newJSDoc.split(`
474
1412
  `);
475
1413
  for (const line of oldLines) {
476
- log(chalk3.red(` - ${line}`));
1414
+ log(chalk5.red(` - ${line}`));
477
1415
  }
478
1416
  for (const line of newLines) {
479
- log(chalk3.green(` + ${line}`));
1417
+ log(chalk5.green(` + ${line}`));
480
1418
  }
481
1419
  } else {
482
1420
  const newLines = edit.newJSDoc.split(`
483
1421
  `);
484
1422
  for (const line of newLines) {
485
- log(chalk3.green(` + ${line}`));
1423
+ log(chalk5.green(` + ${line}`));
486
1424
  }
487
1425
  }
488
1426
  log("");
489
- log(chalk3.dim(` Fixes: ${fixes.map((f) => f.description).join(", ")}`));
1427
+ log(chalk5.dim(` Fixes: ${fixes.map((f) => f.description).join(", ")}`));
490
1428
  log("");
491
1429
  }
492
1430
  }
493
1431
  const totalFixes = Array.from(editsByFile.values()).reduce((sum, fileEdits) => sum + fileEdits.reduce((s, e) => s + e.fixes.length, 0), 0);
494
- log(chalk3.yellow(`${totalFixes} fix(es) across ${editsByFile.size} file(s) would be applied.`));
495
- log(chalk3.gray("Run with --fix to apply these changes."));
1432
+ log(chalk5.yellow(`${totalFixes} fix(es) across ${editsByFile.size} file(s) would be applied.`));
1433
+ log(chalk5.gray("Run with --fix to apply these changes."));
496
1434
  }
497
1435
 
498
1436
  // src/commands/check/output.ts
499
1437
  import { generateReportFromDocCov } from "@doccov/sdk";
500
- import chalk5 from "chalk";
501
1438
 
502
1439
  // src/reports/changelog-renderer.ts
503
1440
  function renderChangelog(data, options = {}) {
@@ -1117,18 +2054,18 @@ function getExportKeyword(kind) {
1117
2054
  }
1118
2055
  }
1119
2056
  function formatExportSignature(exp) {
1120
- const prefix = `export ${getExportKeyword(exp.kind)}`;
2057
+ const prefix2 = `export ${getExportKeyword(exp.kind)}`;
1121
2058
  if (exp.kind === "function" && exp.signatures?.[0]) {
1122
2059
  const sig = exp.signatures[0];
1123
2060
  const params = sig.parameters?.map((p) => `${p.name}${p.required === false ? "?" : ""}`).join(", ") ?? "";
1124
2061
  const ret = extractReturnType(sig.returns?.schema);
1125
- return `${prefix} ${exp.name}(${params}): ${ret}`;
2062
+ return `${prefix2} ${exp.name}(${params}): ${ret}`;
1126
2063
  }
1127
2064
  if (exp.kind === "type" || exp.kind === "interface") {
1128
- return `${prefix} ${exp.name}`;
2065
+ return `${prefix2} ${exp.name}`;
1129
2066
  }
1130
2067
  if (exp.kind === "class") {
1131
- return `${prefix} ${exp.name}`;
2068
+ return `${prefix2} ${exp.name}`;
1132
2069
  }
1133
2070
  return `export ${exp.kind} ${exp.name}`;
1134
2071
  }
@@ -1322,7 +2259,7 @@ function computeStats(openpkg, doccov) {
1322
2259
  import * as fs3 from "node:fs";
1323
2260
  import * as path5 from "node:path";
1324
2261
  import { DEFAULT_REPORT_DIR, getReportPath } from "@doccov/sdk";
1325
- import chalk4 from "chalk";
2262
+ import chalk6 from "chalk";
1326
2263
  function writeReport(options) {
1327
2264
  const { format, content, outputPath, cwd = process.cwd(), silent = false } = options;
1328
2265
  const reportPath = outputPath ? path5.resolve(cwd, outputPath) : path5.resolve(cwd, getReportPath(format));
@@ -1333,7 +2270,7 @@ function writeReport(options) {
1333
2270
  fs3.writeFileSync(reportPath, content);
1334
2271
  const relativePath = path5.relative(cwd, reportPath);
1335
2272
  if (!silent) {
1336
- console.log(chalk4.green(`✓ Wrote ${format} report to ${relativePath}`));
2273
+ console.log(chalk6.green(`✓ Wrote ${format} report to ${relativePath}`));
1337
2274
  }
1338
2275
  return { path: reportPath, format, relativePath };
1339
2276
  }
@@ -1371,6 +2308,7 @@ function displayTextOutput(options, deps) {
1371
2308
  specInfos
1372
2309
  } = options;
1373
2310
  const { log } = deps;
2311
+ const sym = getSymbols(supportsUnicode());
1374
2312
  const totalExportsForDrift = openpkg.exports?.length ?? 0;
1375
2313
  const exportsWithDrift = new Set(driftExports.map((d) => d.name)).size;
1376
2314
  const driftScore = totalExportsForDrift === 0 ? 0 : Math.round(exportsWithDrift / totalExportsForDrift * 100);
@@ -1380,15 +2318,15 @@ function displayTextOutput(options, deps) {
1380
2318
  if (specWarnings.length > 0 || specInfos.length > 0) {
1381
2319
  log("");
1382
2320
  for (const diag of specWarnings) {
1383
- log(chalk5.yellow(`⚠ ${diag.message}`));
2321
+ log(colors.warning(`${sym.warning} ${diag.message}`));
1384
2322
  if (diag.suggestion) {
1385
- log(chalk5.gray(` ${diag.suggestion}`));
2323
+ log(colors.muted(` ${diag.suggestion}`));
1386
2324
  }
1387
2325
  }
1388
2326
  for (const diag of specInfos) {
1389
- log(chalk5.cyan(`ℹ ${diag.message}`));
2327
+ log(colors.info(`${sym.info} ${diag.message}`));
1390
2328
  if (diag.suggestion) {
1391
- log(chalk5.gray(` ${diag.suggestion}`));
2329
+ log(colors.muted(` ${diag.suggestion}`));
1392
2330
  }
1393
2331
  }
1394
2332
  }
@@ -1396,47 +2334,47 @@ function displayTextOutput(options, deps) {
1396
2334
  const pkgVersion = openpkg.meta?.version ?? "";
1397
2335
  const totalExports = openpkg.exports?.length ?? 0;
1398
2336
  log("");
1399
- log(chalk5.bold(`${pkgName}${pkgVersion ? `@${pkgVersion}` : ""}`));
2337
+ log(colors.bold(`${pkgName}${pkgVersion ? `@${pkgVersion}` : ""}`));
1400
2338
  log("");
1401
- log(` Exports: ${totalExports}`);
1402
- if (coverageFailed) {
1403
- log(chalk5.red(` Coverage: ✗ ${coverageScore}%`) + chalk5.dim(` (min ${minCoverage}%)`));
1404
- } else {
1405
- log(chalk5.green(` Coverage: ✓ ${coverageScore}%`) + chalk5.dim(` (min ${minCoverage}%)`));
1406
- }
2339
+ const summaryBuilder = summary({ keyWidth: 10 });
2340
+ summaryBuilder.addKeyValue("Exports", totalExports);
2341
+ summaryBuilder.addKeyValue("Coverage", `${coverageScore}%`, coverageFailed ? "fail" : "pass");
1407
2342
  if (maxDrift !== undefined) {
1408
- if (driftFailed) {
1409
- log(chalk5.red(` Drift: ✗ ${driftScore}%`) + chalk5.dim(` (max ${maxDrift}%)`));
1410
- } else {
1411
- log(chalk5.green(` Drift: ✓ ${driftScore}%`) + chalk5.dim(` (max ${maxDrift}%)`));
1412
- }
2343
+ summaryBuilder.addKeyValue("Drift", `${driftScore}%`, driftFailed ? "fail" : "pass");
1413
2344
  } else {
1414
- log(` Drift: ${driftScore}%`);
2345
+ summaryBuilder.addKeyValue("Drift", `${driftScore}%`);
1415
2346
  }
1416
2347
  if (exampleResult) {
1417
2348
  const typecheckCount = exampleResult.typecheck?.errors.length ?? 0;
1418
2349
  if (typecheckCount > 0) {
1419
- log(chalk5.yellow(` Examples: ${typecheckCount} type error(s)`));
1420
- for (const err of typecheckErrors.slice(0, 5)) {
1421
- const loc = `example[${err.error.exampleIndex}]:${err.error.line}:${err.error.column}`;
1422
- log(chalk5.dim(` ${err.exportName} ${loc}`));
1423
- log(chalk5.red(` ${err.error.message}`));
1424
- }
1425
- if (typecheckErrors.length > 5) {
1426
- log(chalk5.dim(` ... and ${typecheckErrors.length - 5} more`));
1427
- }
2350
+ summaryBuilder.addKeyValue("Examples", `${typecheckCount} type error(s)`, "warn");
1428
2351
  } else {
1429
- log(chalk5.green(` Examples: ✓ validated`));
2352
+ summaryBuilder.addKeyValue("Examples", "validated", "pass");
1430
2353
  }
1431
2354
  }
1432
2355
  const hasStaleRefs = staleRefs.length > 0;
1433
2356
  if (hasStaleRefs) {
1434
- log(chalk5.yellow(` Docs: ${staleRefs.length} stale ref(s)`));
2357
+ summaryBuilder.addKeyValue("Docs", `${staleRefs.length} stale ref(s)`, "warn");
2358
+ }
2359
+ summaryBuilder.print();
2360
+ if (hasTypecheckErrors) {
2361
+ log("");
2362
+ for (const err of typecheckErrors.slice(0, 5)) {
2363
+ const loc = `example[${err.error.exampleIndex}]:${err.error.line}:${err.error.column}`;
2364
+ log(colors.muted(` ${err.exportName} ${loc}`));
2365
+ log(colors.error(` ${err.error.message}`));
2366
+ }
2367
+ if (typecheckErrors.length > 5) {
2368
+ log(colors.muted(` ... and ${typecheckErrors.length - 5} more`));
2369
+ }
2370
+ }
2371
+ if (hasStaleRefs) {
2372
+ log("");
1435
2373
  for (const ref of staleRefs.slice(0, 5)) {
1436
- log(chalk5.dim(` ${ref.file}:${ref.line} - "${ref.exportName}"`));
2374
+ log(colors.muted(` ${ref.file}:${ref.line} - "${ref.exportName}"`));
1437
2375
  }
1438
2376
  if (staleRefs.length > 5) {
1439
- log(chalk5.dim(` ... and ${staleRefs.length - 5} more`));
2377
+ log(colors.muted(` ... and ${staleRefs.length - 5} more`));
1440
2378
  }
1441
2379
  }
1442
2380
  log("");
@@ -1447,17 +2385,23 @@ function displayTextOutput(options, deps) {
1447
2385
  if (maxDrift !== undefined) {
1448
2386
  thresholdParts.push(`drift ${driftScore}% ≤ ${maxDrift}%`);
1449
2387
  }
1450
- log(chalk5.green(`✓ Check passed (${thresholdParts.join(", ")})`));
2388
+ log(colors.success(`${sym.success} Check passed (${thresholdParts.join(", ")})`));
1451
2389
  return true;
1452
2390
  }
2391
+ if (coverageFailed) {
2392
+ log(colors.error(`${sym.error} Coverage ${coverageScore}% below minimum ${minCoverage}%`));
2393
+ }
2394
+ if (driftFailed) {
2395
+ log(colors.error(`${sym.error} Drift ${driftScore}% exceeds maximum ${maxDrift}%`));
2396
+ }
1453
2397
  if (hasTypecheckErrors) {
1454
- log(chalk5.red(`✗ ${typecheckErrors.length} example type errors`));
2398
+ log(colors.error(`${sym.error} ${typecheckErrors.length} example type errors`));
1455
2399
  }
1456
2400
  if (hasStaleRefs) {
1457
- log(chalk5.red(`✗ ${staleRefs.length} stale references in docs`));
2401
+ log(colors.error(`${sym.error} ${staleRefs.length} stale references in docs`));
1458
2402
  }
1459
2403
  log("");
1460
- log(chalk5.dim("Use --format json or --format markdown for detailed reports"));
2404
+ log(colors.muted("Use --format json or --format markdown for detailed reports"));
1461
2405
  return false;
1462
2406
  }
1463
2407
  function handleNonTextOutput(options, deps) {
@@ -1608,18 +2552,9 @@ function registerCheckCommand(program, dependencies = {}) {
1608
2552
  return n;
1609
2553
  }).option("--no-cache", "Bypass spec cache and force regeneration").option("--visibility <tags>", "Filter by release stage: public,beta,alpha,internal (comma-separated)").action(async (entry, options) => {
1610
2554
  try {
2555
+ const spin = spinner("Analyzing...");
1611
2556
  let validations = parseExamplesFlag(options.examples);
1612
2557
  let hasExamples = validations.length > 0;
1613
- const stepList = [
1614
- { label: "Resolved target", activeLabel: "Resolving target" },
1615
- { label: "Loaded config", activeLabel: "Loading config" },
1616
- { label: "Generated spec", activeLabel: "Generating spec" },
1617
- { label: "Enriched spec", activeLabel: "Enriching spec" },
1618
- ...hasExamples ? [{ label: "Validated examples", activeLabel: "Validating examples" }] : [],
1619
- { label: "Processed results", activeLabel: "Processing results" }
1620
- ];
1621
- const steps = new StepProgress(stepList);
1622
- steps.start();
1623
2558
  const fileSystem = new NodeFileSystem(options.cwd);
1624
2559
  const resolved = await resolveTarget(fileSystem, {
1625
2560
  cwd: options.cwd,
@@ -1627,7 +2562,6 @@ function registerCheckCommand(program, dependencies = {}) {
1627
2562
  entry
1628
2563
  });
1629
2564
  const { targetDir, entryFile } = resolved;
1630
- steps.next();
1631
2565
  const config = await loadDocCovConfig(targetDir);
1632
2566
  if (!hasExamples && config?.check?.examples) {
1633
2567
  const configExamples = config.check.examples;
@@ -1650,9 +2584,8 @@ function registerCheckCommand(program, dependencies = {}) {
1650
2584
  };
1651
2585
  const resolvedFilters = mergeFilterOptions(config, cliFilters);
1652
2586
  if (resolvedFilters.visibility) {
1653
- log(chalk6.dim(`Filtering by visibility: ${resolvedFilters.visibility.join(", ")}`));
2587
+ log(chalk7.dim(`Filtering by visibility: ${resolvedFilters.visibility.join(", ")}`));
1654
2588
  }
1655
- steps.next();
1656
2589
  const resolveExternalTypes = !options.skipResolve;
1657
2590
  const analyzer = createDocCov({
1658
2591
  resolveExternalTypes,
@@ -1663,9 +2596,9 @@ function registerCheckCommand(program, dependencies = {}) {
1663
2596
  const analyzeOptions = resolvedFilters.visibility ? { filters: { visibility: resolvedFilters.visibility } } : {};
1664
2597
  const specResult = await analyzer.analyzeFileWithDiagnostics(entryFile, analyzeOptions);
1665
2598
  if (!specResult) {
2599
+ spin.fail("Analysis failed");
1666
2600
  throw new Error("Failed to analyze documentation coverage.");
1667
2601
  }
1668
- steps.next();
1669
2602
  const openpkg = specResult.spec;
1670
2603
  const doccov = buildDocCovSpec({
1671
2604
  openpkg,
@@ -1673,7 +2606,6 @@ function registerCheckCommand(program, dependencies = {}) {
1673
2606
  packagePath: targetDir
1674
2607
  });
1675
2608
  const format = options.format ?? "text";
1676
- steps.next();
1677
2609
  const specWarnings = specResult.diagnostics.filter((d) => d.severity === "warning");
1678
2610
  const specInfos = specResult.diagnostics.filter((d) => d.severity === "info");
1679
2611
  const isPreview = options.preview;
@@ -1689,7 +2621,6 @@ function registerCheckCommand(program, dependencies = {}) {
1689
2621
  exampleResult = validation.result;
1690
2622
  typecheckErrors = validation.typecheckErrors;
1691
2623
  runtimeDrifts = validation.runtimeDrifts;
1692
- steps.next();
1693
2624
  }
1694
2625
  let docsPatterns = options.docs;
1695
2626
  if (docsPatterns.length === 0 && config?.docs?.include) {
@@ -1709,7 +2640,7 @@ function registerCheckCommand(program, dependencies = {}) {
1709
2640
  driftExports = driftExports.filter((d) => !fixResult.fixedDriftKeys.has(`${d.name}:${d.issue}`));
1710
2641
  }
1711
2642
  }
1712
- steps.complete("Check complete");
2643
+ spin.success("Analysis complete");
1713
2644
  if (format !== "text") {
1714
2645
  const passed2 = handleNonTextOutput({
1715
2646
  format,
@@ -1747,7 +2678,7 @@ function registerCheckCommand(program, dependencies = {}) {
1747
2678
  process.exit(1);
1748
2679
  }
1749
2680
  } catch (commandError) {
1750
- error(chalk6.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
2681
+ error(chalk7.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
1751
2682
  process.exit(1);
1752
2683
  }
1753
2684
  });
@@ -1765,7 +2696,7 @@ import {
1765
2696
  parseMarkdownFiles as parseMarkdownFiles2
1766
2697
  } from "@doccov/sdk";
1767
2698
  import { calculateNextVersion, recommendSemverBump } from "@openpkg-ts/spec";
1768
- import chalk7 from "chalk";
2699
+ import chalk8 from "chalk";
1769
2700
  import { glob as glob2 } from "glob";
1770
2701
  var defaultDependencies2 = {
1771
2702
  readFileSync: fs4.readFileSync,
@@ -1800,6 +2731,7 @@ function registerDiffCommand(program, dependencies = {}) {
1800
2731
  ` + `Usage: doccov diff <base> <head>
1801
2732
  ` + " or: doccov diff --base main.json --head feature.json");
1802
2733
  }
2734
+ const spin = spinner("Comparing specs...");
1803
2735
  const baseSpec = loadSpec(baseFile, readFileSync3);
1804
2736
  const headSpec = loadSpec(headFile, readFileSync3);
1805
2737
  const config = await loadDocCovConfig(options.cwd);
@@ -1823,6 +2755,7 @@ function registerDiffCommand(program, dependencies = {}) {
1823
2755
  const minCoverage = resolveThreshold(options.minCoverage, config?.check?.minCoverage);
1824
2756
  const maxDrift = resolveThreshold(options.maxDrift, config?.check?.maxDrift);
1825
2757
  if (options.recommendVersion) {
2758
+ spin.success("Comparison complete");
1826
2759
  const recommendation = recommendSemverBump(diff);
1827
2760
  const currentVersion = headSpec.meta?.version ?? "0.0.0";
1828
2761
  const nextVersion = calculateNextVersion(currentVersion, recommendation.bump);
@@ -1838,9 +2771,9 @@ function registerDiffCommand(program, dependencies = {}) {
1838
2771
  }, null, 2));
1839
2772
  } else {
1840
2773
  log("");
1841
- log(chalk7.bold("Semver Recommendation"));
2774
+ log(chalk8.bold("Semver Recommendation"));
1842
2775
  log(` Current version: ${currentVersion}`);
1843
- log(` Recommended: ${chalk7.cyan(nextVersion)} (${chalk7.yellow(recommendation.bump.toUpperCase())})`);
2776
+ log(` Recommended: ${chalk8.cyan(nextVersion)} (${chalk8.yellow(recommendation.bump.toUpperCase())})`);
1844
2777
  log(` Reason: ${recommendation.reason}`);
1845
2778
  }
1846
2779
  return;
@@ -1855,6 +2788,7 @@ function registerDiffCommand(program, dependencies = {}) {
1855
2788
  headName,
1856
2789
  ...diff
1857
2790
  };
2791
+ spin.success("Comparison complete");
1858
2792
  switch (format) {
1859
2793
  case "text":
1860
2794
  printSummary(diff, baseName, headName, fromCache, log);
@@ -1869,8 +2803,8 @@ function registerDiffCommand(program, dependencies = {}) {
1869
2803
  silent: true
1870
2804
  });
1871
2805
  }
1872
- const cacheNote = fromCache ? chalk7.cyan(" (cached)") : "";
1873
- log(chalk7.dim(`Report: ${jsonPath}`) + cacheNote);
2806
+ const cacheNote = fromCache ? chalk8.cyan(" (cached)") : "";
2807
+ log(chalk8.dim(`Report: ${jsonPath}`) + cacheNote);
1874
2808
  }
1875
2809
  break;
1876
2810
  case "json": {
@@ -1965,18 +2899,18 @@ function registerDiffCommand(program, dependencies = {}) {
1965
2899
  checks
1966
2900
  });
1967
2901
  if (failures.length > 0) {
1968
- log(chalk7.red(`
2902
+ log(chalk8.red(`
1969
2903
  ✗ Check failed`));
1970
2904
  for (const f of failures) {
1971
- log(chalk7.red(` - ${f}`));
2905
+ log(chalk8.red(` - ${f}`));
1972
2906
  }
1973
2907
  process.exitCode = 1;
1974
2908
  } else if (options.strict || minCoverage !== undefined || maxDrift !== undefined) {
1975
- log(chalk7.green(`
2909
+ log(chalk8.green(`
1976
2910
  ✓ All checks passed`));
1977
2911
  }
1978
2912
  } catch (commandError) {
1979
- error(chalk7.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
2913
+ error(chalk8.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
1980
2914
  process.exitCode = 1;
1981
2915
  }
1982
2916
  });
@@ -2003,7 +2937,7 @@ async function generateDiff(baseSpec, headSpec, options, config, log) {
2003
2937
  if (!docsPatterns || docsPatterns.length === 0) {
2004
2938
  if (config?.docs?.include) {
2005
2939
  docsPatterns = config.docs.include;
2006
- log(chalk7.gray(`Using docs patterns from config: ${docsPatterns.join(", ")}`));
2940
+ log(chalk8.gray(`Using docs patterns from config: ${docsPatterns.join(", ")}`));
2007
2941
  }
2008
2942
  }
2009
2943
  if (docsPatterns && docsPatterns.length > 0) {
@@ -2026,37 +2960,37 @@ function loadSpec(filePath, readFileSync3) {
2026
2960
  }
2027
2961
  function printSummary(diff, baseName, headName, fromCache, log) {
2028
2962
  log("");
2029
- const cacheIndicator = fromCache ? chalk7.cyan(" (cached)") : "";
2030
- log(chalk7.bold(`Comparing: ${baseName} → ${headName}`) + cacheIndicator);
2963
+ const cacheIndicator = fromCache ? chalk8.cyan(" (cached)") : "";
2964
+ log(chalk8.bold(`Comparing: ${baseName} → ${headName}`) + cacheIndicator);
2031
2965
  log("─".repeat(40));
2032
2966
  log("");
2033
- const coverageColor = diff.coverageDelta > 0 ? chalk7.green : diff.coverageDelta < 0 ? chalk7.red : chalk7.gray;
2967
+ const coverageColor = diff.coverageDelta > 0 ? chalk8.green : diff.coverageDelta < 0 ? chalk8.red : chalk8.gray;
2034
2968
  const coverageSign = diff.coverageDelta > 0 ? "+" : "";
2035
2969
  log(` Coverage: ${diff.oldCoverage}% → ${diff.newCoverage}% ${coverageColor(`(${coverageSign}${diff.coverageDelta}%)`)}`);
2036
2970
  const breakingCount = diff.breaking.length;
2037
2971
  const highSeverity = diff.categorizedBreaking?.filter((c) => c.severity === "high").length ?? 0;
2038
2972
  if (breakingCount > 0) {
2039
- const severityNote = highSeverity > 0 ? chalk7.red(` (${highSeverity} high severity)`) : "";
2040
- log(` Breaking: ${chalk7.red(breakingCount)} changes${severityNote}`);
2973
+ const severityNote = highSeverity > 0 ? chalk8.red(` (${highSeverity} high severity)`) : "";
2974
+ log(` Breaking: ${chalk8.red(breakingCount)} changes${severityNote}`);
2041
2975
  } else {
2042
- log(` Breaking: ${chalk7.green("0")} changes`);
2976
+ log(` Breaking: ${chalk8.green("0")} changes`);
2043
2977
  }
2044
2978
  const newCount = diff.nonBreaking.length;
2045
2979
  const undocCount = diff.newUndocumented.length;
2046
2980
  if (newCount > 0) {
2047
- const undocNote = undocCount > 0 ? chalk7.yellow(` (${undocCount} undocumented)`) : "";
2048
- log(` New: ${chalk7.green(newCount)} exports${undocNote}`);
2981
+ const undocNote = undocCount > 0 ? chalk8.yellow(` (${undocCount} undocumented)`) : "";
2982
+ log(` New: ${chalk8.green(newCount)} exports${undocNote}`);
2049
2983
  }
2050
2984
  if (diff.driftIntroduced > 0 || diff.driftResolved > 0) {
2051
2985
  const parts = [];
2052
2986
  if (diff.driftIntroduced > 0)
2053
- parts.push(chalk7.red(`+${diff.driftIntroduced}`));
2987
+ parts.push(chalk8.red(`+${diff.driftIntroduced}`));
2054
2988
  if (diff.driftResolved > 0)
2055
- parts.push(chalk7.green(`-${diff.driftResolved}`));
2989
+ parts.push(chalk8.green(`-${diff.driftResolved}`));
2056
2990
  log(` Drift: ${parts.join(", ")}`);
2057
2991
  }
2058
2992
  const recommendation = recommendSemverBump(diff);
2059
- const bumpColor = recommendation.bump === "major" ? chalk7.red : recommendation.bump === "minor" ? chalk7.yellow : chalk7.green;
2993
+ const bumpColor = recommendation.bump === "major" ? chalk8.red : recommendation.bump === "minor" ? chalk8.yellow : chalk8.green;
2060
2994
  log(` Semver: ${bumpColor(recommendation.bump.toUpperCase())} (${recommendation.reason})`);
2061
2995
  log("");
2062
2996
  }
@@ -2084,8 +3018,8 @@ function validateDiff(diff, headSpec, options) {
2084
3018
  failures.push(`${diff.newUndocumented.length} undocumented export(s)`);
2085
3019
  }
2086
3020
  if (checks.has("docs-impact") && hasDocsImpact(diff)) {
2087
- const summary = getDocsImpactSummary(diff);
2088
- failures.push(`${summary.totalIssues} docs issue(s)`);
3021
+ const summary2 = getDocsImpactSummary(diff);
3022
+ failures.push(`${summary2.totalIssues} docs issue(s)`);
2089
3023
  }
2090
3024
  return failures;
2091
3025
  }
@@ -2138,9 +3072,10 @@ function printGitHubAnnotations(diff, log) {
2138
3072
 
2139
3073
  // src/commands/info.ts
2140
3074
  import { buildDocCovSpec as buildDocCovSpec2, DocCov as DocCov2, NodeFileSystem as NodeFileSystem2, resolveTarget as resolveTarget2 } from "@doccov/sdk";
2141
- import chalk8 from "chalk";
3075
+ import chalk9 from "chalk";
2142
3076
  function registerInfoCommand(program) {
2143
3077
  program.command("info [entry]").description("Show brief documentation coverage summary").option("--cwd <dir>", "Working directory", process.cwd()).option("--package <name>", "Target package name (for monorepos)").option("--skip-resolve", "Skip external type resolution from node_modules").action(async (entry, options) => {
3078
+ const spin = spinner("Analyzing documentation coverage");
2144
3079
  try {
2145
3080
  const fileSystem = new NodeFileSystem2(options.cwd);
2146
3081
  const resolved = await resolveTarget2(fileSystem, {
@@ -2155,6 +3090,7 @@ function registerInfoCommand(program) {
2155
3090
  });
2156
3091
  const specResult = await analyzer.analyzeFileWithDiagnostics(entryFile);
2157
3092
  if (!specResult) {
3093
+ spin.fail("Failed to analyze");
2158
3094
  throw new Error("Failed to analyze documentation coverage.");
2159
3095
  }
2160
3096
  const openpkg = specResult.spec;
@@ -2164,15 +3100,17 @@ function registerInfoCommand(program) {
2164
3100
  packagePath: targetDir
2165
3101
  });
2166
3102
  const stats = computeStats(openpkg, doccov);
3103
+ spin.success("Analysis complete");
2167
3104
  console.log("");
2168
- console.log(chalk8.bold(`${stats.packageName}@${stats.version}`));
3105
+ console.log(chalk9.bold(`${stats.packageName}@${stats.version}`));
2169
3106
  console.log("");
2170
- console.log(` Exports: ${chalk8.bold(stats.totalExports.toString())}`);
2171
- console.log(` Coverage: ${chalk8.bold(`${stats.coverageScore}%`)}`);
2172
- console.log(` Drift: ${chalk8.bold(`${stats.driftScore}%`)}`);
3107
+ console.log(` Exports: ${chalk9.bold(stats.totalExports.toString())}`);
3108
+ console.log(` Coverage: ${chalk9.bold(`${stats.coverageScore}%`)}`);
3109
+ console.log(` Drift: ${chalk9.bold(`${stats.driftScore}%`)}`);
2173
3110
  console.log("");
2174
3111
  } catch (err) {
2175
- console.error(chalk8.red("Error:"), err instanceof Error ? err.message : err);
3112
+ spin.fail("Analysis failed");
3113
+ console.error(chalk9.red("Error:"), err instanceof Error ? err.message : err);
2176
3114
  process.exit(1);
2177
3115
  }
2178
3116
  });
@@ -2181,7 +3119,7 @@ function registerInfoCommand(program) {
2181
3119
  // src/commands/init.ts
2182
3120
  import * as fs5 from "node:fs";
2183
3121
  import * as path7 from "node:path";
2184
- import chalk9 from "chalk";
3122
+ import chalk10 from "chalk";
2185
3123
  var defaultDependencies3 = {
2186
3124
  fileExists: fs5.existsSync,
2187
3125
  writeFileSync: fs5.writeFileSync,
@@ -2199,7 +3137,7 @@ function registerInitCommand(program, dependencies = {}) {
2199
3137
  const cwd = path7.resolve(options.cwd);
2200
3138
  const existing = findExistingConfig(cwd, fileExists2);
2201
3139
  if (existing) {
2202
- error(chalk9.red(`A DocCov config already exists at ${path7.relative(cwd, existing) || "./doccov.config.*"}.`));
3140
+ error(chalk10.red(`A DocCov config already exists at ${path7.relative(cwd, existing) || "./doccov.config.*"}.`));
2203
3141
  process.exitCode = 1;
2204
3142
  return;
2205
3143
  }
@@ -2208,36 +3146,37 @@ function registerInitCommand(program, dependencies = {}) {
2208
3146
  const fileName = `doccov.config.${targetFormat}`;
2209
3147
  const outputPath = path7.join(cwd, fileName);
2210
3148
  if (fileExists2(outputPath)) {
2211
- error(chalk9.red(`Cannot create ${fileName}; file already exists.`));
3149
+ error(chalk10.red(`Cannot create ${fileName}; file already exists.`));
2212
3150
  process.exitCode = 1;
2213
3151
  return;
2214
3152
  }
3153
+ const sym = getSymbols(supportsUnicode());
2215
3154
  const template = buildConfigTemplate();
2216
3155
  writeFileSync3(outputPath, template, { encoding: "utf8" });
2217
- log(chalk9.green(`✓ Created ${fileName}`));
3156
+ log(colors.success(`${sym.success} Created ${fileName}`));
2218
3157
  if (!options.skipAction) {
2219
3158
  const workflowDir = path7.join(cwd, ".github", "workflows");
2220
3159
  const workflowPath = path7.join(workflowDir, "doccov.yml");
2221
3160
  if (!fileExists2(workflowPath)) {
2222
3161
  mkdirSync3(workflowDir, { recursive: true });
2223
3162
  writeFileSync3(workflowPath, buildWorkflowTemplate(), { encoding: "utf8" });
2224
- log(chalk9.green(`✓ Created .github/workflows/doccov.yml`));
3163
+ log(colors.success(`${sym.success} Created .github/workflows/doccov.yml`));
2225
3164
  } else {
2226
- log(chalk9.yellow(` Skipped .github/workflows/doccov.yml (already exists)`));
3165
+ log(colors.warning(`${sym.bullet} Skipped .github/workflows/doccov.yml (already exists)`));
2227
3166
  }
2228
3167
  }
2229
3168
  const repoInfo = detectRepoInfo(cwd, fileExists2, readFileSync4);
2230
3169
  log("");
2231
- log(chalk9.bold("Add this badge to your README:"));
3170
+ log(chalk10.bold("Add this badge to your README:"));
2232
3171
  log("");
2233
3172
  if (repoInfo) {
2234
- log(chalk9.cyan(`[![DocCov](https://doccov.dev/badge/${repoInfo.owner}/${repoInfo.repo})](https://doccov.dev/${repoInfo.owner}/${repoInfo.repo})`));
3173
+ log(chalk10.cyan(`[![DocCov](https://doccov.dev/badge/${repoInfo.owner}/${repoInfo.repo})](https://doccov.dev/${repoInfo.owner}/${repoInfo.repo})`));
2235
3174
  } else {
2236
- log(chalk9.cyan(`[![DocCov](https://doccov.dev/badge/OWNER/REPO)](https://doccov.dev/OWNER/REPO)`));
2237
- log(chalk9.dim(" Replace OWNER/REPO with your GitHub repo"));
3175
+ log(chalk10.cyan(`[![DocCov](https://doccov.dev/badge/OWNER/REPO)](https://doccov.dev/OWNER/REPO)`));
3176
+ log(chalk10.dim(" Replace OWNER/REPO with your GitHub repo"));
2238
3177
  }
2239
3178
  log("");
2240
- log(chalk9.dim("Run `doccov check` to verify your documentation coverage"));
3179
+ log(chalk10.dim("Run `doccov check` to verify your documentation coverage"));
2241
3180
  });
2242
3181
  }
2243
3182
  var findExistingConfig = (cwd, fileExists2) => {
@@ -2373,9 +3312,9 @@ import {
2373
3312
  } from "@doccov/sdk";
2374
3313
  import { validateDocCovSpec } from "@doccov/spec";
2375
3314
  import { normalize, validateSpec } from "@openpkg-ts/spec";
2376
- import chalk10 from "chalk";
3315
+ import chalk11 from "chalk";
2377
3316
  // package.json
2378
- var version = "0.25.6";
3317
+ var version = "0.25.8";
2379
3318
 
2380
3319
  // src/commands/spec.ts
2381
3320
  var defaultDependencies4 = {
@@ -2387,12 +3326,12 @@ var defaultDependencies4 = {
2387
3326
  function getArrayLength(value) {
2388
3327
  return Array.isArray(value) ? value.length : 0;
2389
3328
  }
2390
- function formatDiagnosticOutput(prefix, diagnostic, baseDir) {
3329
+ function formatDiagnosticOutput(prefix2, diagnostic, baseDir) {
2391
3330
  const location = diagnostic.location;
2392
3331
  const relativePath = location?.file ? path8.relative(baseDir, location.file) || location.file : undefined;
2393
- const locationText = location && relativePath ? chalk10.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
3332
+ const locationText = location && relativePath ? chalk11.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
2394
3333
  const locationPrefix = locationText ? `${locationText} ` : "";
2395
- return `${prefix} ${locationPrefix}${diagnostic.message}`;
3334
+ return `${prefix2} ${locationPrefix}${diagnostic.message}`;
2396
3335
  }
2397
3336
  function registerSpecCommand(program, dependencies = {}) {
2398
3337
  const { createDocCov, writeFileSync: writeFileSync4, log, error } = {
@@ -2401,18 +3340,7 @@ function registerSpecCommand(program, dependencies = {}) {
2401
3340
  };
2402
3341
  program.command("spec [entry]").description("Generate OpenPkg + DocCov specifications").option("--cwd <dir>", "Working directory", process.cwd()).option("-p, --package <name>", "Target package name (for monorepos)").option("-o, --output <dir>", "Output directory", ".doccov").option("-f, --format <format>", "Output format: json (default) or api-surface", "json").option("--openpkg-only", "Only generate openpkg.json (skip coverage analysis)").option("--include <patterns>", "Include exports matching pattern (comma-separated)").option("--exclude <patterns>", "Exclude exports matching pattern (comma-separated)").option("--visibility <tags>", "Filter by release stage: public,beta,alpha,internal (comma-separated)").option("--skip-resolve", "Skip external type resolution from node_modules").option("--max-type-depth <n>", "Maximum depth for type conversion", "20").option("--runtime", "Enable Standard Schema runtime extraction (richer output for Zod, Valibot, etc.)").option("--no-cache", "Bypass spec cache and force regeneration").option("--show-diagnostics", "Show TypeScript compiler diagnostics").option("--verbose", "Show detailed generation metadata").action(async (entry, options) => {
2403
3342
  try {
2404
- const stepsList = [
2405
- { label: "Resolved target", activeLabel: "Resolving target" },
2406
- { label: "Loaded config", activeLabel: "Loading config" },
2407
- { label: "Generated spec", activeLabel: "Generating spec" },
2408
- { label: "Validated schema", activeLabel: "Validating schema" }
2409
- ];
2410
- if (!options.openpkgOnly) {
2411
- stepsList.push({ label: "Built coverage analysis", activeLabel: "Building coverage" });
2412
- }
2413
- stepsList.push({ label: "Wrote output", activeLabel: "Writing output" });
2414
- const steps = new StepProgress(stepsList);
2415
- steps.start();
3343
+ const spin = spinner("Generating spec...");
2416
3344
  const fileSystem = new NodeFileSystem3(options.cwd);
2417
3345
  const resolved = await resolveTarget3(fileSystem, {
2418
3346
  cwd: options.cwd,
@@ -2420,15 +3348,14 @@ function registerSpecCommand(program, dependencies = {}) {
2420
3348
  entry
2421
3349
  });
2422
3350
  const { targetDir, entryFile, packageInfo, entryPointInfo } = resolved;
2423
- steps.next();
2424
3351
  let config = null;
2425
3352
  try {
2426
3353
  config = await loadDocCovConfig(targetDir);
2427
3354
  } catch (configError) {
2428
- error(chalk10.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
3355
+ spin.fail("Failed to load config");
3356
+ error(chalk11.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
2429
3357
  process.exit(1);
2430
3358
  }
2431
- steps.next();
2432
3359
  const cliFilters = {
2433
3360
  include: parseListFlag(options.include),
2434
3361
  exclude: parseListFlag(options.exclude),
@@ -2463,19 +3390,19 @@ function registerSpecCommand(program, dependencies = {}) {
2463
3390
  } : { generationInput };
2464
3391
  const result = await doccov.analyzeFileWithDiagnostics(entryFile, analyzeOptions);
2465
3392
  if (!result) {
3393
+ spin.fail("Generation failed");
2466
3394
  throw new Error("Failed to produce an OpenPkg spec.");
2467
3395
  }
2468
- steps.next();
2469
3396
  const normalized = normalize(result.spec);
2470
3397
  const validation = validateSpec(normalized);
2471
3398
  if (!validation.ok) {
2472
- error(chalk10.red("Spec failed schema validation"));
3399
+ spin.fail("Validation failed");
3400
+ error(chalk11.red("Spec failed schema validation"));
2473
3401
  for (const err of validation.errors) {
2474
- error(chalk10.red(`schema: ${err.instancePath || "/"} ${err.message}`));
3402
+ error(chalk11.red(`schema: ${err.instancePath || "/"} ${err.message}`));
2475
3403
  }
2476
3404
  process.exit(1);
2477
3405
  }
2478
- steps.next();
2479
3406
  let doccovSpec = null;
2480
3407
  if (!options.openpkgOnly) {
2481
3408
  doccovSpec = buildDocCovSpec3({
@@ -2485,13 +3412,13 @@ function registerSpecCommand(program, dependencies = {}) {
2485
3412
  });
2486
3413
  const doccovValidation = validateDocCovSpec(doccovSpec);
2487
3414
  if (!doccovValidation.ok) {
2488
- error(chalk10.red("DocCov spec failed schema validation"));
3415
+ spin.fail("DocCov validation failed");
3416
+ error(chalk11.red("DocCov spec failed schema validation"));
2489
3417
  for (const err of doccovValidation.errors) {
2490
- error(chalk10.red(`doccov: ${err.instancePath || "/"} ${err.message}`));
3418
+ error(chalk11.red(`doccov: ${err.instancePath || "/"} ${err.message}`));
2491
3419
  }
2492
3420
  process.exit(1);
2493
3421
  }
2494
- steps.next();
2495
3422
  }
2496
3423
  const format = options.format ?? "json";
2497
3424
  const outputDir = path8.resolve(options.cwd, options.output);
@@ -2500,20 +3427,20 @@ function registerSpecCommand(program, dependencies = {}) {
2500
3427
  const apiSurface = renderApiSurface(normalized);
2501
3428
  const apiSurfacePath = path8.join(outputDir, "api-surface.txt");
2502
3429
  writeFileSync4(apiSurfacePath, apiSurface);
2503
- steps.complete(`Generated ${options.output}/ (API surface)`);
3430
+ spin.success(`Generated ${options.output}/ (API surface)`);
2504
3431
  } else {
2505
3432
  const openpkgPath = path8.join(outputDir, "openpkg.json");
2506
3433
  writeFileSync4(openpkgPath, JSON.stringify(normalized, null, 2));
2507
3434
  if (doccovSpec) {
2508
3435
  const doccovPath = path8.join(outputDir, "doccov.json");
2509
3436
  writeFileSync4(doccovPath, JSON.stringify(doccovSpec, null, 2));
2510
- steps.complete(`Generated ${options.output}/`);
2511
- log(chalk10.gray(` openpkg.json: ${getArrayLength(normalized.exports)} exports`));
2512
- log(chalk10.gray(` doccov.json: ${doccovSpec.summary.score}% coverage, ${doccovSpec.summary.drift.total} drift issues`));
3437
+ spin.success(`Generated ${options.output}/`);
3438
+ log(chalk11.gray(` openpkg.json: ${getArrayLength(normalized.exports)} exports`));
3439
+ log(chalk11.gray(` doccov.json: ${doccovSpec.summary.score}% coverage, ${doccovSpec.summary.drift.total} drift issues`));
2513
3440
  } else {
2514
- steps.complete(`Generated ${options.output}/openpkg.json`);
2515
- log(chalk10.gray(` ${getArrayLength(normalized.exports)} exports`));
2516
- log(chalk10.gray(` ${getArrayLength(normalized.types)} types`));
3441
+ spin.success(`Generated ${options.output}/openpkg.json`);
3442
+ log(chalk11.gray(` ${getArrayLength(normalized.exports)} exports`));
3443
+ log(chalk11.gray(` ${getArrayLength(normalized.types)} types`));
2517
3444
  }
2518
3445
  }
2519
3446
  const schemaExtraction = normalized.generation?.analysis?.schemaExtraction;
@@ -2521,63 +3448,63 @@ function registerSpecCommand(program, dependencies = {}) {
2521
3448
  const pm = await detectPackageManager(fileSystem);
2522
3449
  const buildCmd = pm.name === "npm" ? "npm run build" : `${pm.name} run build`;
2523
3450
  log("");
2524
- log(chalk10.yellow("⚠ Runtime extraction requested but no schemas extracted."));
2525
- log(chalk10.yellow(` Ensure project is built (${buildCmd}) and dist/ exists.`));
3451
+ log(chalk11.yellow("⚠ Runtime extraction requested but no schemas extracted."));
3452
+ log(chalk11.yellow(` Ensure project is built (${buildCmd}) and dist/ exists.`));
2526
3453
  }
2527
3454
  if (options.verbose && normalized.generation) {
2528
3455
  const gen = normalized.generation;
2529
3456
  log("");
2530
- log(chalk10.bold("Generation Info"));
2531
- log(chalk10.gray(` Timestamp: ${gen.timestamp}`));
2532
- log(chalk10.gray(` Generator: ${gen.generator.name}@${gen.generator.version}`));
2533
- log(chalk10.gray(` Entry point: ${gen.analysis.entryPoint}`));
2534
- log(chalk10.gray(` Detected via: ${gen.analysis.entryPointSource}`));
2535
- log(chalk10.gray(` Declaration only: ${gen.analysis.isDeclarationOnly ? "yes" : "no"}`));
2536
- log(chalk10.gray(` External types: ${gen.analysis.resolvedExternalTypes ? "resolved" : "skipped"}`));
3457
+ log(chalk11.bold("Generation Info"));
3458
+ log(chalk11.gray(` Timestamp: ${gen.timestamp}`));
3459
+ log(chalk11.gray(` Generator: ${gen.generator.name}@${gen.generator.version}`));
3460
+ log(chalk11.gray(` Entry point: ${gen.analysis.entryPoint}`));
3461
+ log(chalk11.gray(` Detected via: ${gen.analysis.entryPointSource}`));
3462
+ log(chalk11.gray(` Declaration only: ${gen.analysis.isDeclarationOnly ? "yes" : "no"}`));
3463
+ log(chalk11.gray(` External types: ${gen.analysis.resolvedExternalTypes ? "resolved" : "skipped"}`));
2537
3464
  if (gen.analysis.maxTypeDepth) {
2538
- log(chalk10.gray(` Max type depth: ${gen.analysis.maxTypeDepth}`));
3465
+ log(chalk11.gray(` Max type depth: ${gen.analysis.maxTypeDepth}`));
2539
3466
  }
2540
3467
  if (gen.analysis.schemaExtraction) {
2541
3468
  const se = gen.analysis.schemaExtraction;
2542
- log(chalk10.gray(` Schema extraction: ${se.method}`));
3469
+ log(chalk11.gray(` Schema extraction: ${se.method}`));
2543
3470
  if (se.runtimeCount) {
2544
- log(chalk10.gray(` Runtime schemas: ${se.runtimeCount} (${se.vendors?.join(", ")})`));
3471
+ log(chalk11.gray(` Runtime schemas: ${se.runtimeCount} (${se.vendors?.join(", ")})`));
2545
3472
  }
2546
3473
  }
2547
3474
  log("");
2548
- log(chalk10.bold("Environment"));
2549
- log(chalk10.gray(` node_modules: ${gen.environment.hasNodeModules ? "found" : "not found"}`));
3475
+ log(chalk11.bold("Environment"));
3476
+ log(chalk11.gray(` node_modules: ${gen.environment.hasNodeModules ? "found" : "not found"}`));
2550
3477
  if (gen.environment.packageManager) {
2551
- log(chalk10.gray(` Package manager: ${gen.environment.packageManager}`));
3478
+ log(chalk11.gray(` Package manager: ${gen.environment.packageManager}`));
2552
3479
  }
2553
3480
  if (gen.environment.isMonorepo) {
2554
- log(chalk10.gray(` Monorepo: yes`));
3481
+ log(chalk11.gray(` Monorepo: yes`));
2555
3482
  }
2556
3483
  if (gen.environment.targetPackage) {
2557
- log(chalk10.gray(` Target package: ${gen.environment.targetPackage}`));
3484
+ log(chalk11.gray(` Target package: ${gen.environment.targetPackage}`));
2558
3485
  }
2559
3486
  if (gen.issues.length > 0) {
2560
3487
  log("");
2561
- log(chalk10.bold("Issues"));
3488
+ log(chalk11.bold("Issues"));
2562
3489
  for (const issue of gen.issues) {
2563
- const prefix = issue.severity === "error" ? chalk10.red(">") : issue.severity === "warning" ? chalk10.yellow(">") : chalk10.cyan(">");
2564
- log(`${prefix} [${issue.code}] ${issue.message}`);
3490
+ const prefix2 = issue.severity === "error" ? chalk11.red(">") : issue.severity === "warning" ? chalk11.yellow(">") : chalk11.cyan(">");
3491
+ log(`${prefix2} [${issue.code}] ${issue.message}`);
2565
3492
  if (issue.suggestion) {
2566
- log(chalk10.gray(` ${issue.suggestion}`));
3493
+ log(chalk11.gray(` ${issue.suggestion}`));
2567
3494
  }
2568
3495
  }
2569
3496
  }
2570
3497
  }
2571
3498
  if (options.showDiagnostics && result.diagnostics.length > 0) {
2572
3499
  log("");
2573
- log(chalk10.bold("Diagnostics"));
3500
+ log(chalk11.bold("Diagnostics"));
2574
3501
  for (const diagnostic of result.diagnostics) {
2575
- const prefix = diagnostic.severity === "error" ? chalk10.red(">") : diagnostic.severity === "warning" ? chalk10.yellow(">") : chalk10.cyan(">");
2576
- log(formatDiagnosticOutput(prefix, diagnostic, targetDir));
3502
+ const prefix2 = diagnostic.severity === "error" ? chalk11.red(">") : diagnostic.severity === "warning" ? chalk11.yellow(">") : chalk11.cyan(">");
3503
+ log(formatDiagnosticOutput(prefix2, diagnostic, targetDir));
2577
3504
  }
2578
3505
  }
2579
3506
  } catch (commandError) {
2580
- error(chalk10.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
3507
+ error(chalk11.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
2581
3508
  process.exit(1);
2582
3509
  }
2583
3510
  });
@@ -2597,7 +3524,7 @@ import {
2597
3524
  renderSparkline,
2598
3525
  saveSnapshot
2599
3526
  } from "@doccov/sdk";
2600
- import chalk11 from "chalk";
3527
+ import chalk12 from "chalk";
2601
3528
  function formatDate(timestamp) {
2602
3529
  const date = new Date(timestamp);
2603
3530
  return date.toLocaleDateString("en-US", {
@@ -2610,19 +3537,19 @@ function formatDate(timestamp) {
2610
3537
  }
2611
3538
  function getColorForScore(score) {
2612
3539
  if (score >= 90)
2613
- return chalk11.green;
3540
+ return chalk12.green;
2614
3541
  if (score >= 70)
2615
- return chalk11.yellow;
3542
+ return chalk12.yellow;
2616
3543
  if (score >= 50)
2617
- return chalk11.hex("#FFA500");
2618
- return chalk11.red;
3544
+ return chalk12.hex("#FFA500");
3545
+ return chalk12.red;
2619
3546
  }
2620
3547
  function formatSnapshot(snapshot) {
2621
3548
  const color = getColorForScore(snapshot.coverageScore);
2622
3549
  const date = formatDate(snapshot.timestamp);
2623
3550
  const version2 = snapshot.version ? ` v${snapshot.version}` : "";
2624
- const commit = snapshot.commit ? chalk11.gray(` (${snapshot.commit.slice(0, 7)})`) : "";
2625
- return `${chalk11.gray(date)} ${color(`${snapshot.coverageScore}%`)} ${snapshot.documentedExports}/${snapshot.totalExports} exports${version2}${commit}`;
3551
+ const commit = snapshot.commit ? chalk12.gray(` (${snapshot.commit.slice(0, 7)})`) : "";
3552
+ return `${chalk12.gray(date)} ${color(`${snapshot.coverageScore}%`)} ${snapshot.documentedExports}/${snapshot.totalExports} exports${version2}${commit}`;
2626
3553
  }
2627
3554
  function formatWeekDate(timestamp) {
2628
3555
  const date = new Date(timestamp);
@@ -2630,10 +3557,10 @@ function formatWeekDate(timestamp) {
2630
3557
  }
2631
3558
  function formatVelocity(velocity) {
2632
3559
  if (velocity > 0)
2633
- return chalk11.green(`+${velocity}%/day`);
3560
+ return chalk12.green(`+${velocity}%/day`);
2634
3561
  if (velocity < 0)
2635
- return chalk11.red(`${velocity}%/day`);
2636
- return chalk11.gray("0%/day");
3562
+ return chalk12.red(`${velocity}%/day`);
3563
+ return chalk12.gray("0%/day");
2637
3564
  }
2638
3565
  function registerTrendsCommand(program) {
2639
3566
  program.command("trends").description("Show coverage trends over time").option("--cwd <dir>", "Working directory", process.cwd()).option("-n, --limit <count>", "Number of snapshots to show", "10").option("--prune <count>", "Prune history to keep only N snapshots").option("--record", "Record current coverage to history").option("--json", "Output as JSON").option("--extended", "Show extended trend analysis (velocity, projections)").option("--tier <tier>", "Retention tier: free (7d), team (30d), pro (90d)", "pro").option("--weekly", "Show weekly summary breakdown").action(async (options) => {
@@ -2643,42 +3570,44 @@ function registerTrendsCommand(program) {
2643
3570
  const keepCount = parseInt(options.prune, 10);
2644
3571
  if (!Number.isNaN(keepCount)) {
2645
3572
  const deleted = pruneHistory(cwd, keepCount);
2646
- console.log(chalk11.green(`Pruned ${deleted} old snapshots, kept ${keepCount} most recent`));
3573
+ console.log(chalk12.green(`Pruned ${deleted} old snapshots, kept ${keepCount} most recent`));
2647
3574
  } else {
2648
3575
  const deleted = pruneByTier(cwd, tier);
2649
- console.log(chalk11.green(`Pruned ${deleted} snapshots older than ${RETENTION_DAYS[tier]} days`));
3576
+ console.log(chalk12.green(`Pruned ${deleted} snapshots older than ${RETENTION_DAYS[tier]} days`));
2650
3577
  }
2651
3578
  return;
2652
3579
  }
2653
3580
  if (options.record) {
2654
3581
  const specPath = path9.resolve(cwd, "openpkg.json");
2655
3582
  if (!fs7.existsSync(specPath)) {
2656
- console.error(chalk11.red("No openpkg.json found. Run `doccov spec` first to generate a spec."));
3583
+ console.error(chalk12.red("No openpkg.json found. Run `doccov spec` first to generate a spec."));
2657
3584
  process.exit(1);
2658
3585
  }
3586
+ const spin = spinner("Recording coverage snapshot");
2659
3587
  try {
2660
3588
  const specContent = fs7.readFileSync(specPath, "utf-8");
2661
3589
  const spec = JSON.parse(specContent);
2662
3590
  const trend = getTrend(spec, cwd);
2663
3591
  saveSnapshot(trend.current, cwd);
2664
- console.log(chalk11.green("Recorded coverage snapshot:"));
3592
+ spin.success("Recorded coverage snapshot");
2665
3593
  console.log(formatSnapshot(trend.current));
2666
3594
  if (trend.delta !== undefined) {
2667
3595
  const deltaStr = formatDelta2(trend.delta);
2668
- const deltaColor = trend.delta > 0 ? chalk11.green : trend.delta < 0 ? chalk11.red : chalk11.gray;
2669
- console.log(chalk11.gray("Change from previous:"), deltaColor(deltaStr));
3596
+ const deltaColor = trend.delta > 0 ? chalk12.green : trend.delta < 0 ? chalk12.red : chalk12.gray;
3597
+ console.log(chalk12.gray("Change from previous:"), deltaColor(deltaStr));
2670
3598
  }
2671
3599
  return;
2672
3600
  } catch (error) {
2673
- console.error(chalk11.red("Failed to read openpkg.json:"), error instanceof Error ? error.message : error);
3601
+ spin.fail("Failed to record snapshot");
3602
+ console.error(chalk12.red("Failed to read openpkg.json:"), error instanceof Error ? error.message : error);
2674
3603
  process.exit(1);
2675
3604
  }
2676
3605
  }
2677
3606
  const snapshots = loadSnapshots(cwd);
2678
3607
  const limit = parseInt(options.limit ?? "10", 10);
2679
3608
  if (snapshots.length === 0) {
2680
- console.log(chalk11.yellow("No coverage history found."));
2681
- console.log(chalk11.gray("Run `doccov trends --record` to save the current coverage."));
3609
+ console.log(chalk12.yellow("No coverage history found."));
3610
+ console.log(chalk12.gray("Run `doccov trends --record` to save the current coverage."));
2682
3611
  return;
2683
3612
  }
2684
3613
  if (options.json) {
@@ -2693,17 +3622,17 @@ function registerTrendsCommand(program) {
2693
3622
  }
2694
3623
  const sparklineData = snapshots.slice(0, 10).map((s) => s.coverageScore).reverse();
2695
3624
  const sparkline = renderSparkline(sparklineData);
2696
- console.log(chalk11.bold("Coverage Trends"));
2697
- console.log(chalk11.gray(`Package: ${snapshots[0].package}`));
2698
- console.log(chalk11.gray(`Sparkline: ${sparkline}`));
3625
+ console.log(chalk12.bold("Coverage Trends"));
3626
+ console.log(chalk12.gray(`Package: ${snapshots[0].package}`));
3627
+ console.log(chalk12.gray(`Sparkline: ${sparkline}`));
2699
3628
  console.log("");
2700
3629
  if (snapshots.length >= 2) {
2701
3630
  const oldest = snapshots[snapshots.length - 1];
2702
3631
  const newest = snapshots[0];
2703
3632
  const overallDelta = newest.coverageScore - oldest.coverageScore;
2704
3633
  const deltaStr = formatDelta2(overallDelta);
2705
- const deltaColor = overallDelta > 0 ? chalk11.green : overallDelta < 0 ? chalk11.red : chalk11.gray;
2706
- console.log(chalk11.gray("Overall trend:"), deltaColor(deltaStr), chalk11.gray(`(${snapshots.length} snapshots)`));
3634
+ const deltaColor = overallDelta > 0 ? chalk12.green : overallDelta < 0 ? chalk12.red : chalk12.gray;
3635
+ console.log(chalk12.gray("Overall trend:"), deltaColor(deltaStr), chalk12.gray(`(${snapshots.length} snapshots)`));
2707
3636
  console.log("");
2708
3637
  }
2709
3638
  if (options.extended) {
@@ -2713,8 +3642,8 @@ function registerTrendsCommand(program) {
2713
3642
  const specContent = fs7.readFileSync(specPath, "utf-8");
2714
3643
  const spec = JSON.parse(specContent);
2715
3644
  const extended = getExtendedTrend(spec, cwd, { tier });
2716
- console.log(chalk11.bold("Extended Analysis"));
2717
- console.log(chalk11.gray(`Tier: ${tier} (${RETENTION_DAYS[tier]}-day retention)`));
3645
+ console.log(chalk12.bold("Extended Analysis"));
3646
+ console.log(chalk12.gray(`Tier: ${tier} (${RETENTION_DAYS[tier]}-day retention)`));
2718
3647
  console.log("");
2719
3648
  console.log(" Velocity:");
2720
3649
  console.log(` 7-day: ${formatVelocity(extended.velocity7d)}`);
@@ -2723,47 +3652,47 @@ function registerTrendsCommand(program) {
2723
3652
  console.log(` 90-day: ${formatVelocity(extended.velocity90d)}`);
2724
3653
  }
2725
3654
  console.log("");
2726
- const projColor = extended.projected30d >= extended.trend.current.coverageScore ? chalk11.green : chalk11.red;
3655
+ const projColor = extended.projected30d >= extended.trend.current.coverageScore ? chalk12.green : chalk12.red;
2727
3656
  console.log(` Projected (30d): ${projColor(`${extended.projected30d}%`)}`);
2728
- console.log(` All-time high: ${chalk11.green(`${extended.allTimeHigh}%`)}`);
2729
- console.log(` All-time low: ${chalk11.red(`${extended.allTimeLow}%`)}`);
3657
+ console.log(` All-time high: ${chalk12.green(`${extended.allTimeHigh}%`)}`);
3658
+ console.log(` All-time low: ${chalk12.red(`${extended.allTimeLow}%`)}`);
2730
3659
  if (extended.dataRange) {
2731
3660
  const startDate = formatWeekDate(extended.dataRange.start);
2732
3661
  const endDate = formatWeekDate(extended.dataRange.end);
2733
- console.log(chalk11.gray(` Data range: ${startDate} - ${endDate}`));
3662
+ console.log(chalk12.gray(` Data range: ${startDate} - ${endDate}`));
2734
3663
  }
2735
3664
  console.log("");
2736
3665
  if (options.weekly && extended.weeklySummaries.length > 0) {
2737
- console.log(chalk11.bold("Weekly Summary"));
3666
+ console.log(chalk12.bold("Weekly Summary"));
2738
3667
  const weekLimit = Math.min(extended.weeklySummaries.length, 8);
2739
3668
  for (let i = 0;i < weekLimit; i++) {
2740
3669
  const week = extended.weeklySummaries[i];
2741
3670
  const weekStart = formatWeekDate(week.weekStart);
2742
3671
  const weekEnd = formatWeekDate(week.weekEnd);
2743
- const deltaColor = week.delta > 0 ? chalk11.green : week.delta < 0 ? chalk11.red : chalk11.gray;
3672
+ const deltaColor = week.delta > 0 ? chalk12.green : week.delta < 0 ? chalk12.red : chalk12.gray;
2744
3673
  const deltaStr = week.delta > 0 ? `+${week.delta}%` : `${week.delta}%`;
2745
3674
  console.log(` ${weekStart} - ${weekEnd}: ${week.avgCoverage}% avg ${deltaColor(deltaStr)} (${week.snapshotCount} snapshots)`);
2746
3675
  }
2747
3676
  if (extended.weeklySummaries.length > weekLimit) {
2748
- console.log(chalk11.gray(` ... and ${extended.weeklySummaries.length - weekLimit} more weeks`));
3677
+ console.log(chalk12.gray(` ... and ${extended.weeklySummaries.length - weekLimit} more weeks`));
2749
3678
  }
2750
3679
  console.log("");
2751
3680
  }
2752
3681
  } catch {
2753
- console.log(chalk11.yellow("Could not load openpkg.json for extended analysis"));
3682
+ console.log(chalk12.yellow("Could not load openpkg.json for extended analysis"));
2754
3683
  console.log("");
2755
3684
  }
2756
3685
  }
2757
3686
  }
2758
- console.log(chalk11.bold("History"));
3687
+ console.log(chalk12.bold("History"));
2759
3688
  const displaySnapshots = snapshots.slice(0, limit);
2760
3689
  for (let i = 0;i < displaySnapshots.length; i++) {
2761
3690
  const snapshot = displaySnapshots[i];
2762
- const prefix = i === 0 ? chalk11.cyan("→") : " ";
2763
- console.log(`${prefix} ${formatSnapshot(snapshot)}`);
3691
+ const prefix2 = i === 0 ? chalk12.cyan("→") : " ";
3692
+ console.log(`${prefix2} ${formatSnapshot(snapshot)}`);
2764
3693
  }
2765
3694
  if (snapshots.length > limit) {
2766
- console.log(chalk11.gray(` ... and ${snapshots.length - limit} more`));
3695
+ console.log(chalk12.gray(` ... and ${snapshots.length - limit} more`));
2767
3696
  }
2768
3697
  });
2769
3698
  }