@doccov/cli 0.5.2 → 0.5.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.
Files changed (2) hide show
  1. package/dist/cli.js +83 -73
  2. package/package.json +1 -2
package/dist/cli.js CHANGED
@@ -170,7 +170,6 @@ import {
170
170
  serializeJSDoc
171
171
  } from "@doccov/sdk";
172
172
  import chalk from "chalk";
173
- import ora from "ora";
174
173
 
175
174
  // src/utils/llm-assertion-parser.ts
176
175
  import { createAnthropic } from "@ai-sdk/anthropic";
@@ -243,11 +242,6 @@ async function parseAssertionsWithLLM(code) {
243
242
  // src/commands/check.ts
244
243
  var defaultDependencies = {
245
244
  createDocCov: (options) => new DocCov(options),
246
- spinner: (text) => ora({
247
- text,
248
- discardStdin: false,
249
- hideCursor: true
250
- }),
251
245
  log: console.log,
252
246
  error: console.error
253
247
  };
@@ -276,7 +270,7 @@ function groupByExport(drifts) {
276
270
  return map;
277
271
  }
278
272
  function registerCheckCommand(program, dependencies = {}) {
279
- const { createDocCov, spinner, log, error } = {
273
+ const { createDocCov, log, error } = {
280
274
  ...defaultDependencies,
281
275
  ...dependencies
282
276
  };
@@ -315,15 +309,17 @@ function registerCheckCommand(program, dependencies = {}) {
315
309
  }
316
310
  const minCoverage = clampCoverage(options.minCoverage ?? 80);
317
311
  const resolveExternalTypes = !options.skipResolve;
318
- const spinnerInstance = spinner("Analyzing documentation coverage...");
319
- spinnerInstance.start();
312
+ process.stdout.write(chalk.cyan(`> Analyzing documentation coverage...
313
+ `));
320
314
  let specResult;
321
315
  try {
322
316
  const doccov = createDocCov({ resolveExternalTypes });
323
317
  specResult = await doccov.analyzeFileWithDiagnostics(entryFile);
324
- spinnerInstance.succeed("Documentation analysis complete");
318
+ process.stdout.write(chalk.green(`✓ Documentation analysis complete
319
+ `));
325
320
  } catch (analysisError) {
326
- spinnerInstance.fail("Failed to analyze documentation coverage");
321
+ process.stdout.write(chalk.red(`✗ Failed to analyze documentation coverage
322
+ `));
327
323
  throw analysisError;
328
324
  }
329
325
  if (!specResult) {
@@ -359,8 +355,8 @@ function registerCheckCommand(program, dependencies = {}) {
359
355
  if (allExamples.length === 0) {
360
356
  log(chalk.gray("No @example blocks found"));
361
357
  } else {
362
- const examplesSpinner = spinner("Installing package for examples...");
363
- examplesSpinner.start();
358
+ process.stdout.write(chalk.cyan(`> Installing package for examples...
359
+ `));
364
360
  const flatExamples = allExamples.flatMap((e) => e.examples);
365
361
  const packageResult = await runExamplesWithPackage(flatExamples, {
366
362
  packagePath: targetDir,
@@ -369,10 +365,12 @@ function registerCheckCommand(program, dependencies = {}) {
369
365
  cwd: targetDir
370
366
  });
371
367
  if (!packageResult.installSuccess) {
372
- examplesSpinner.fail(`Package install failed: ${packageResult.installError}`);
368
+ process.stdout.write(chalk.red(`✗ Package install failed: ${packageResult.installError}
369
+ `));
373
370
  log(chalk.yellow("Skipping example execution. Ensure the package is built."));
374
371
  } else {
375
- examplesSpinner.text = "Running @example blocks...";
372
+ process.stdout.write(chalk.cyan(`> Running @example blocks...
373
+ `));
376
374
  let examplesRun = 0;
377
375
  let examplesFailed = 0;
378
376
  let exampleIndex = 0;
@@ -442,9 +440,11 @@ function registerCheckCommand(program, dependencies = {}) {
442
440
  }
443
441
  }
444
442
  if (examplesFailed > 0) {
445
- examplesSpinner.fail(`${examplesFailed}/${examplesRun} example(s) failed`);
443
+ process.stdout.write(chalk.red(`✗ ${examplesFailed}/${examplesRun} example(s) failed
444
+ `));
446
445
  } else {
447
- examplesSpinner.succeed(`${examplesRun} example(s) passed`);
446
+ process.stdout.write(chalk.green(`✓ ${examplesRun} example(s) passed
447
+ `));
448
448
  }
449
449
  }
450
450
  }
@@ -538,16 +538,18 @@ function registerCheckCommand(program, dependencies = {}) {
538
538
  }
539
539
  log(chalk.gray("Run without --dry-run to apply these changes."));
540
540
  } else {
541
- const applySpinner = spinner("Applying fixes...");
542
- applySpinner.start();
541
+ process.stdout.write(chalk.cyan(`> Applying fixes...
542
+ `));
543
543
  const applyResult = await applyEdits(edits);
544
544
  if (applyResult.errors.length > 0) {
545
- applySpinner.warn("Some fixes could not be applied");
545
+ process.stdout.write(chalk.yellow(`⚠ Some fixes could not be applied
546
+ `));
546
547
  for (const err of applyResult.errors) {
547
548
  error(chalk.red(` ${err.file}: ${err.error}`));
548
549
  }
549
550
  } else {
550
- applySpinner.succeed(`Applied ${applyResult.editsApplied} fix(es) to ${applyResult.filesModified} file(s)`);
551
+ process.stdout.write(chalk.green(`✓ Applied ${applyResult.editsApplied} fix(es) to ${applyResult.filesModified} file(s)
552
+ `));
551
553
  }
552
554
  log("");
553
555
  for (const [filePath, fileEdits] of editsByFile) {
@@ -943,7 +945,6 @@ import {
943
945
  } from "@doccov/sdk";
944
946
  import { normalize, validateSpec } from "@openpkg-ts/spec";
945
947
  import chalk4 from "chalk";
946
- import ora2 from "ora";
947
948
 
948
949
  // src/utils/filter-options.ts
949
950
  import chalk3 from "chalk";
@@ -999,11 +1000,6 @@ var mergeFilterOptions = (config, cliOptions) => {
999
1000
  var defaultDependencies3 = {
1000
1001
  createDocCov: (options) => new DocCov2(options),
1001
1002
  writeFileSync: fs3.writeFileSync,
1002
- spinner: (text) => ora2({
1003
- text,
1004
- discardStdin: false,
1005
- hideCursor: true
1006
- }),
1007
1003
  log: console.log,
1008
1004
  error: console.error
1009
1005
  };
@@ -1028,7 +1024,7 @@ function formatDiagnosticOutput(prefix, diagnostic, baseDir) {
1028
1024
  return `${prefix} ${locationPrefix}${diagnostic.message}`;
1029
1025
  }
1030
1026
  function registerGenerateCommand(program, dependencies = {}) {
1031
- const { createDocCov, writeFileSync: writeFileSync2, spinner, log, error } = {
1027
+ const { createDocCov, writeFileSync: writeFileSync2, log, error } = {
1032
1028
  ...defaultDependencies3,
1033
1029
  ...dependencies
1034
1030
  };
@@ -1083,8 +1079,8 @@ function registerGenerateCommand(program, dependencies = {}) {
1083
1079
  for (const message of resolvedFilters.messages) {
1084
1080
  log(chalk4.gray(`• ${message}`));
1085
1081
  }
1086
- const spinnerInstance = spinner("Generating OpenPkg spec...");
1087
- spinnerInstance.start();
1082
+ process.stdout.write(chalk4.cyan(`> Generating OpenPkg spec...
1083
+ `));
1088
1084
  let result;
1089
1085
  try {
1090
1086
  const doccov = createDocCov({
@@ -1097,9 +1093,11 @@ function registerGenerateCommand(program, dependencies = {}) {
1097
1093
  }
1098
1094
  } : {};
1099
1095
  result = await doccov.analyzeFileWithDiagnostics(entryFile, analyzeOptions);
1100
- spinnerInstance.succeed("Generated OpenPkg spec");
1096
+ process.stdout.write(chalk4.green(`✓ Generated OpenPkg spec
1097
+ `));
1101
1098
  } catch (generationError) {
1102
- spinnerInstance.fail("Failed to generate spec");
1099
+ process.stdout.write(chalk4.red(`✗ Failed to generate spec
1100
+ `));
1103
1101
  throw generationError;
1104
1102
  }
1105
1103
  if (!result) {
@@ -1278,7 +1276,6 @@ import {
1278
1276
  NodeFileSystem as NodeFileSystem3
1279
1277
  } from "@doccov/sdk";
1280
1278
  import chalk6 from "chalk";
1281
- import ora3 from "ora";
1282
1279
 
1283
1280
  // src/reports/markdown.ts
1284
1281
  function bar(pct, width = 10) {
@@ -1480,16 +1477,20 @@ function registerReportCommand(program) {
1480
1477
  } else {
1481
1478
  entryFile = path6.resolve(targetDir, entryFile);
1482
1479
  }
1483
- const spinner = ora3({
1484
- text: "Analyzing...",
1485
- discardStdin: false,
1486
- hideCursor: true
1487
- }).start();
1488
- const resolveExternalTypes = !options.skipResolve;
1489
- const doccov = new DocCov3({ resolveExternalTypes });
1490
- const result = await doccov.analyzeFileWithDiagnostics(entryFile);
1491
- spinner.succeed("Analysis complete");
1492
- spec = result.spec;
1480
+ process.stdout.write(chalk6.cyan(`> Analyzing...
1481
+ `));
1482
+ try {
1483
+ const resolveExternalTypes = !options.skipResolve;
1484
+ const doccov = new DocCov3({ resolveExternalTypes });
1485
+ const result = await doccov.analyzeFileWithDiagnostics(entryFile);
1486
+ process.stdout.write(chalk6.green(`✓ Analysis complete
1487
+ `));
1488
+ spec = result.spec;
1489
+ } catch (analysisError) {
1490
+ process.stdout.write(chalk6.red(`✗ Analysis failed
1491
+ `));
1492
+ throw analysisError;
1493
+ }
1493
1494
  }
1494
1495
  const stats = computeStats(spec);
1495
1496
  const format = options.output;
@@ -1532,7 +1533,6 @@ import {
1532
1533
  NodeFileSystem as NodeFileSystem4
1533
1534
  } from "@doccov/sdk";
1534
1535
  import chalk7 from "chalk";
1535
- import ora4 from "ora";
1536
1536
  import { simpleGit } from "simple-git";
1537
1537
 
1538
1538
  // src/utils/github-url.ts
@@ -1660,16 +1660,11 @@ async function generateBuildPlan(repoDir) {
1660
1660
  // src/commands/scan.ts
1661
1661
  var defaultDependencies5 = {
1662
1662
  createDocCov: (options) => new DocCov4(options),
1663
- spinner: (text) => ora4({
1664
- text,
1665
- discardStdin: false,
1666
- hideCursor: true
1667
- }),
1668
1663
  log: console.log,
1669
1664
  error: console.error
1670
1665
  };
1671
1666
  function registerScanCommand(program, dependencies = {}) {
1672
- const { createDocCov, spinner, log, error } = {
1667
+ const { createDocCov, log, error } = {
1673
1668
  ...defaultDependencies5,
1674
1669
  ...dependencies
1675
1670
  };
@@ -1685,8 +1680,8 @@ function registerScanCommand(program, dependencies = {}) {
1685
1680
  log("");
1686
1681
  tempDir = path8.join(os.tmpdir(), `doccov-scan-${Date.now()}-${Math.random().toString(36).slice(2)}`);
1687
1682
  fs7.mkdirSync(tempDir, { recursive: true });
1688
- const cloneSpinner = spinner(`Cloning ${parsed.owner}/${parsed.repo}...`);
1689
- cloneSpinner.start();
1683
+ process.stdout.write(chalk7.cyan(`> Cloning ${parsed.owner}/${parsed.repo}...
1684
+ `));
1690
1685
  try {
1691
1686
  const git = simpleGit({
1692
1687
  timeout: {
@@ -1707,9 +1702,11 @@ function registerScanCommand(program, dependencies = {}) {
1707
1702
  } finally {
1708
1703
  process.env = originalEnv;
1709
1704
  }
1710
- cloneSpinner.succeed(`Cloned ${parsed.owner}/${parsed.repo}`);
1705
+ process.stdout.write(chalk7.green(`✓ Cloned ${parsed.owner}/${parsed.repo}
1706
+ `));
1711
1707
  } catch (cloneError) {
1712
- cloneSpinner.fail("Failed to clone repository");
1708
+ process.stdout.write(chalk7.red(`✗ Failed to clone repository
1709
+ `));
1713
1710
  const message = cloneError instanceof Error ? cloneError.message : String(cloneError);
1714
1711
  if (message.includes("Authentication failed") || message.includes("could not read Username") || message.includes("terminal prompts disabled") || message.includes("Invalid username or password") || message.includes("Permission denied")) {
1715
1712
  throw new Error(`Authentication required: This repository appears to be private. ` + `Public repositories only are currently supported.
@@ -1728,8 +1725,8 @@ function registerScanCommand(program, dependencies = {}) {
1728
1725
  if (options.skipInstall) {
1729
1726
  log(chalk7.gray("Skipping dependency installation (--skip-install)"));
1730
1727
  } else {
1731
- const installSpinner = spinner("Installing dependencies...");
1732
- installSpinner.start();
1728
+ process.stdout.write(chalk7.cyan(`> Installing dependencies...
1729
+ `));
1733
1730
  const installErrors = [];
1734
1731
  try {
1735
1732
  const { execSync } = await import("node:child_process");
@@ -1778,16 +1775,19 @@ function registerScanCommand(program, dependencies = {}) {
1778
1775
  }
1779
1776
  }
1780
1777
  if (installed) {
1781
- installSpinner.succeed("Dependencies installed");
1778
+ process.stdout.write(chalk7.green(`✓ Dependencies installed
1779
+ `));
1782
1780
  } else {
1783
- installSpinner.warn("Could not install dependencies (analysis may be limited)");
1781
+ process.stdout.write(chalk7.yellow(`⚠ Could not install dependencies (analysis may be limited)
1782
+ `));
1784
1783
  for (const err of installErrors) {
1785
1784
  log(chalk7.gray(` ${err}`));
1786
1785
  }
1787
1786
  }
1788
1787
  } catch (outerError) {
1789
1788
  const msg = outerError instanceof Error ? outerError.message : String(outerError);
1790
- installSpinner.warn(`Could not install dependencies: ${msg.slice(0, 100)}`);
1789
+ process.stdout.write(chalk7.yellow(`⚠ Could not install dependencies: ${msg.slice(0, 100)}
1790
+ `));
1791
1791
  for (const err of installErrors) {
1792
1792
  log(chalk7.gray(` ${err}`));
1793
1793
  }
@@ -1818,13 +1818,14 @@ function registerScanCommand(program, dependencies = {}) {
1818
1818
  packageName = pkg.name;
1819
1819
  log(chalk7.gray(`Analyzing package: ${packageName}`));
1820
1820
  }
1821
- const entrySpinner = spinner("Detecting entry point...");
1822
- entrySpinner.start();
1821
+ process.stdout.write(chalk7.cyan(`> Detecting entry point...
1822
+ `));
1823
1823
  let entryPath;
1824
1824
  const targetFs = mono.isMonorepo ? new NodeFileSystem4(targetDir) : fileSystem;
1825
1825
  let buildFailed = false;
1826
1826
  const runLlmFallback = async (reason) => {
1827
- entrySpinner.text = `${reason}, trying LLM fallback...`;
1827
+ process.stdout.write(chalk7.cyan(`> ${reason}, trying LLM fallback...
1828
+ `));
1828
1829
  const plan = await generateBuildPlan(targetDir);
1829
1830
  if (!plan) {
1830
1831
  return null;
@@ -1858,45 +1859,54 @@ function registerScanCommand(program, dependencies = {}) {
1858
1859
  const buildInfo = await detectBuildInfo(targetFs);
1859
1860
  const needsBuildStep = entry.isDeclarationOnly && buildInfo.exoticIndicators.wasm;
1860
1861
  if (needsBuildStep) {
1861
- entrySpinner.text = "Detected .d.ts entry with WASM indicators...";
1862
+ process.stdout.write(chalk7.cyan(`> Detected .d.ts entry with WASM indicators...
1863
+ `));
1862
1864
  const llmEntry = await runLlmFallback("WASM project detected");
1863
1865
  if (llmEntry) {
1864
1866
  entryPath = path8.join(targetDir, llmEntry);
1865
1867
  if (buildFailed) {
1866
- entrySpinner.succeed(`Entry point: ${llmEntry} (using pre-committed declarations)`);
1868
+ process.stdout.write(chalk7.green(`✓ Entry point: ${llmEntry} (using pre-committed declarations)
1869
+ `));
1867
1870
  log(chalk7.gray(" Coverage may be limited - generated .d.ts files typically lack JSDoc"));
1868
1871
  } else {
1869
- entrySpinner.succeed(`Entry point: ${llmEntry} (from LLM fallback - WASM project)`);
1872
+ process.stdout.write(chalk7.green(`✓ Entry point: ${llmEntry} (from LLM fallback - WASM project)
1873
+ `));
1870
1874
  }
1871
1875
  } else {
1872
1876
  entryPath = path8.join(targetDir, entry.path);
1873
- entrySpinner.succeed(`Entry point: ${entry.path} (from ${entry.source})`);
1877
+ process.stdout.write(chalk7.green(`✓ Entry point: ${entry.path} (from ${entry.source})
1878
+ `));
1874
1879
  log(chalk7.yellow(" ⚠ WASM project detected but no API key - analysis may be limited"));
1875
1880
  }
1876
1881
  } else {
1877
1882
  entryPath = path8.join(targetDir, entry.path);
1878
- entrySpinner.succeed(`Entry point: ${entry.path} (from ${entry.source})`);
1883
+ process.stdout.write(chalk7.green(`✓ Entry point: ${entry.path} (from ${entry.source})
1884
+ `));
1879
1885
  }
1880
1886
  } catch (entryError) {
1881
1887
  const llmEntry = await runLlmFallback("Heuristics failed");
1882
1888
  if (llmEntry) {
1883
1889
  entryPath = path8.join(targetDir, llmEntry);
1884
- entrySpinner.succeed(`Entry point: ${llmEntry} (from LLM fallback)`);
1890
+ process.stdout.write(chalk7.green(`✓ Entry point: ${llmEntry} (from LLM fallback)
1891
+ `));
1885
1892
  } else {
1886
- entrySpinner.fail("Could not detect entry point (set OPENAI_API_KEY for smart fallback)");
1893
+ process.stdout.write(chalk7.red(`✗ Could not detect entry point (set OPENAI_API_KEY for smart fallback)
1894
+ `));
1887
1895
  throw entryError;
1888
1896
  }
1889
1897
  }
1890
- const analyzeSpinner = spinner("Analyzing documentation coverage...");
1891
- analyzeSpinner.start();
1898
+ process.stdout.write(chalk7.cyan(`> Analyzing documentation coverage...
1899
+ `));
1892
1900
  let result;
1893
1901
  try {
1894
1902
  const resolveExternalTypes = !options.skipResolve;
1895
1903
  const doccov = createDocCov({ resolveExternalTypes });
1896
1904
  result = await doccov.analyzeFileWithDiagnostics(entryPath);
1897
- analyzeSpinner.succeed("Analysis complete");
1905
+ process.stdout.write(chalk7.green(`✓ Analysis complete
1906
+ `));
1898
1907
  } catch (analysisError) {
1899
- analyzeSpinner.fail("Analysis failed");
1908
+ process.stdout.write(chalk7.red(`✗ Analysis failed
1909
+ `));
1900
1910
  throw analysisError;
1901
1911
  }
1902
1912
  const spec = result.spec;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doccov/cli",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "DocCov CLI - Documentation coverage and drift detection for TypeScript",
5
5
  "keywords": [
6
6
  "typescript",
@@ -55,7 +55,6 @@
55
55
  "chalk": "^5.4.1",
56
56
  "commander": "^14.0.0",
57
57
  "glob": "^11.0.0",
58
- "ora": "^8.2.0",
59
58
  "simple-git": "^3.27.0",
60
59
  "zod": "^3.23.8"
61
60
  },