@doccov/cli 0.30.7 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +81 -10
  2. package/dist/cli.js +505 -155
  3. package/package.json +3 -3
package/README.md CHANGED
@@ -37,12 +37,42 @@ doccov info src/index.ts
37
37
  Analyze documentation coverage against thresholds.
38
38
 
39
39
  ```bash
40
- doccov check src/index.ts --min-coverage 80
40
+ doccov check src/index.ts --min-health 80
41
41
  doccov check --format json -o report.json
42
42
  doccov check --examples typecheck # Validate @example blocks
43
43
  doccov check --fix # Auto-fix drift issues
44
44
  ```
45
45
 
46
+ #### Monorepo / Batch Mode
47
+
48
+ Analyze multiple packages at once using glob patterns or multiple targets:
49
+
50
+ ```bash
51
+ # Glob pattern - analyze all packages
52
+ doccov check "packages/*/src/index.ts"
53
+
54
+ # Multiple explicit targets
55
+ doccov check packages/server/src/index.ts packages/client/src/index.ts
56
+
57
+ # With options
58
+ doccov check "packages/*/src/index.ts" --format markdown --min-health 60
59
+ ```
60
+
61
+ Output shows per-package breakdown with aggregated totals:
62
+
63
+ ```
64
+ Documentation Coverage Report (3 packages)
65
+
66
+ | Package | Health | Exports | Drift |
67
+ |---------|--------|---------|-------|
68
+ | @pkg/server | 75% | 78 | 4 |
69
+ | @pkg/client | 82% | 45 | 2 |
70
+ | @pkg/core | 90% | 32 | 1 |
71
+ | Total | 81% | 155 | 7 |
72
+
73
+ ✓ Check passed (health 81% >= 80%)
74
+ ```
75
+
46
76
  ### spec
47
77
 
48
78
  Generate specification files.
@@ -87,17 +117,58 @@ doccov trends --extended # Show velocity/projections
87
117
 
88
118
  ## Configuration
89
119
 
90
- Create `doccov.config.yaml` or use `doccov init`:
120
+ Create `doccov.config.ts` or use `doccov init`:
121
+
122
+ ```ts
123
+ // doccov.config.ts
124
+ import { defineConfig } from '@doccov/cli';
125
+
126
+ export default defineConfig({
127
+ check: {
128
+ minHealth: 80,
129
+ examples: ['presence', 'typecheck'],
130
+
131
+ // Documentation style presets
132
+ style: 'minimal', // 'minimal' | 'verbose' | 'types-only'
133
+
134
+ // Fine-grained requirements (override preset)
135
+ require: {
136
+ description: true,
137
+ params: false,
138
+ returns: false,
139
+ examples: false,
140
+ },
141
+ },
142
+ docs: {
143
+ include: ['docs/**/*.md'],
144
+ },
145
+ });
146
+ ```
147
+
148
+ ### Style Presets
149
+
150
+ Different projects have different documentation standards. Use `style` to choose a preset:
151
+
152
+ | Preset | description | params | returns | examples |
153
+ |--------|-------------|--------|---------|----------|
154
+ | `minimal` | required | optional | optional | optional |
155
+ | `verbose` | required | required | required | optional |
156
+ | `types-only` | optional | optional | optional | optional |
157
+
158
+ - **minimal** (default): Only requires description. Good for projects relying on TypeScript types.
159
+ - **verbose**: Requires description, @param, and @returns. For comprehensive API documentation.
160
+ - **types-only**: No requirements. Score is 100% if exports exist. For TypeScript-first projects.
91
161
 
92
- ```yaml
93
- check:
94
- minCoverage: 80
95
- maxDrift: 10
96
- examples: [presence, typecheck]
162
+ Use `require` to override individual rules from the preset:
97
163
 
98
- docs:
99
- include:
100
- - "docs/**/*.md"
164
+ ```ts
165
+ // Start with minimal, but also require examples
166
+ {
167
+ style: 'minimal',
168
+ require: {
169
+ examples: true,
170
+ }
171
+ }
101
172
  ```
102
173
 
103
174
  ## Output Formats
package/dist/cli.js CHANGED
@@ -75,19 +75,25 @@ ${formatIssues(issues)}`);
75
75
  var defineConfig = (config) => config;
76
76
  // src/cli.ts
77
77
  import { readFileSync as readFileSync7 } from "node:fs";
78
- import * as path12 from "node:path";
78
+ import * as path13 from "node:path";
79
79
  import { fileURLToPath } from "node:url";
80
80
  import { Command } from "commander";
81
81
 
82
82
  // src/commands/check/index.ts
83
+ import * as path8 from "node:path";
83
84
  import {
85
+ aggregateResults,
84
86
  buildDocCovSpec,
87
+ createPackageResult,
85
88
  DocCov,
89
+ findEntryPointForFile,
90
+ IncrementalAnalyzer,
86
91
  NodeFileSystem,
87
92
  parseExamplesFlag,
88
93
  resolveTarget
89
94
  } from "@doccov/sdk";
90
- import chalk6 from "chalk";
95
+ import chalk7 from "chalk";
96
+ import { glob as glob2 } from "glob";
91
97
 
92
98
  // src/utils/filter-options.ts
93
99
  import { mergeFilters, parseListFlag } from "@doccov/sdk";
@@ -1060,6 +1066,37 @@ function renderDiffHtml(data, options = {}) {
1060
1066
  </body>
1061
1067
  </html>`;
1062
1068
  }
1069
+ // src/reports/json.ts
1070
+ function formatCIJson(report, options = {}) {
1071
+ const minHealth = options.minHealth ?? 0;
1072
+ const health = report.health?.score ?? report.coverage.score;
1073
+ const healthPassed = health >= minHealth;
1074
+ const hasTypecheckErrors = options.hasTypecheckErrors ?? false;
1075
+ const hasRuntimeErrors = options.hasRuntimeErrors ?? false;
1076
+ const hasStaleRefs = options.hasStaleRefs ?? false;
1077
+ const success = healthPassed && !hasTypecheckErrors && !hasRuntimeErrors && !hasStaleRefs;
1078
+ return {
1079
+ success,
1080
+ health,
1081
+ thresholds: {
1082
+ health: {
1083
+ min: minHealth,
1084
+ actual: health,
1085
+ passed: healthPassed
1086
+ }
1087
+ },
1088
+ drift: {
1089
+ total: report.coverage.driftCount,
1090
+ fixable: report.coverage.driftSummary?.fixable ?? 0
1091
+ },
1092
+ exports: {
1093
+ total: report.coverage.totalExports,
1094
+ documented: report.coverage.documentedExports
1095
+ },
1096
+ exitCode: success ? 0 : 1,
1097
+ report
1098
+ };
1099
+ }
1063
1100
  // src/reports/markdown.ts
1064
1101
  import { DRIFT_CATEGORY_LABELS } from "@doccov/sdk";
1065
1102
  function bar2(pct, width = 10) {
@@ -1178,18 +1215,18 @@ function renderMarkdown(stats, options = {}) {
1178
1215
  continue;
1179
1216
  lines.push(`### ${DRIFT_CATEGORY_LABELS[category]}`);
1180
1217
  lines.push("");
1181
- lines.push("| Export | Issue |");
1182
- lines.push("|--------|-------|");
1218
+ lines.push("| Export | Issue | Expected | Actual |");
1219
+ lines.push("|--------|-------|----------|--------|");
1183
1220
  for (const d of issues.slice(0, Math.min(limit, 10))) {
1184
1221
  const hint = d.suggestion ? ` → ${d.suggestion}` : "";
1185
- lines.push(`| \`${d.exportName}\` | ${d.issue}${hint} |`);
1222
+ lines.push(`| \`${d.exportName}\` | ${d.issue}${hint} | ${d.expected ?? "-"} | ${d.actual ?? "-"} |`);
1186
1223
  }
1187
1224
  if (issues.length > 10) {
1188
1225
  const moreText = `${issues.length - 10} more ${category} issues`;
1189
1226
  if (reportUrl) {
1190
- lines.push(`| [${moreText} →](${reportUrl}#drift-${category}) | |`);
1227
+ lines.push(`| [${moreText} →](${reportUrl}#drift-${category}) | | | |`);
1191
1228
  } else {
1192
- lines.push(`| ... | ${moreText} |`);
1229
+ lines.push(`| ... | ${moreText} | | |`);
1193
1230
  }
1194
1231
  }
1195
1232
  lines.push("");
@@ -1244,6 +1281,38 @@ function renderMarkdown(stats, options = {}) {
1244
1281
  return lines.join(`
1245
1282
  `);
1246
1283
  }
1284
+ function renderBatchMarkdown(batchResult, options = {}) {
1285
+ const { packages, aggregate } = batchResult;
1286
+ const lines = [];
1287
+ lines.push("# Documentation Coverage Report");
1288
+ lines.push("");
1289
+ lines.push(`## Summary (${packages.length} packages)`);
1290
+ lines.push("");
1291
+ lines.push("| Package | Health | Exports | Drift |");
1292
+ lines.push("|---------|--------|---------|-------|");
1293
+ for (const pkg of packages) {
1294
+ lines.push(`| ${pkg.name} | ${pkg.health}% | ${pkg.totalExports} | ${pkg.driftCount} |`);
1295
+ }
1296
+ lines.push(`| **Total** | **${aggregate.health}%** | **${aggregate.totalExports}** | **${aggregate.driftCount}** |`);
1297
+ lines.push("");
1298
+ for (const pkg of packages) {
1299
+ lines.push(`## ${pkg.name}`);
1300
+ lines.push("");
1301
+ lines.push(`**Health: ${pkg.health}%** \`${bar2(pkg.health)}\``);
1302
+ lines.push("");
1303
+ lines.push("| Metric | Value |");
1304
+ lines.push("|--------|-------|");
1305
+ lines.push(`| Exports | ${pkg.totalExports} |`);
1306
+ lines.push(`| Documented | ${pkg.documented} |`);
1307
+ lines.push(`| Coverage | ${pkg.coverageScore}% |`);
1308
+ lines.push(`| Drift | ${pkg.driftCount} |`);
1309
+ lines.push("");
1310
+ }
1311
+ lines.push("---");
1312
+ lines.push("*Generated by [DocCov](https://doccov.com)*");
1313
+ return lines.join(`
1314
+ `);
1315
+ }
1247
1316
  // src/reports/stats.ts
1248
1317
  import { getExportAnalysis, getExportDrift as getExportDrift2, isFixableDrift } from "@doccov/sdk";
1249
1318
  import {
@@ -1291,6 +1360,8 @@ function computeStats(openpkg, doccov, options = {}) {
1291
1360
  exportName: exp.name,
1292
1361
  type: d.type,
1293
1362
  issue: d.issue,
1363
+ expected: d.expected,
1364
+ actual: d.actual,
1294
1365
  suggestion: d.suggestion
1295
1366
  };
1296
1367
  driftIssues.push(item);
@@ -1632,8 +1703,16 @@ function handleNonTextOutput(options, deps) {
1632
1703
  const stats = computeStats(openpkg, doccov, { staleRefs });
1633
1704
  const report = generateReportFromDocCov(openpkg, doccov);
1634
1705
  const extendedReport = staleRefs.length > 0 ? { ...report, staleRefs } : report;
1635
- const jsonContent = JSON.stringify(extendedReport, null, 2);
1636
- const healthScore = doccov.summary.health?.score ?? stats.coverageScore;
1706
+ const hasTypecheckErrors = typecheckErrors.length > 0;
1707
+ const hasRuntimeErrors = runtimeErrors > 0;
1708
+ const hasStaleRefs = staleRefs.length > 0;
1709
+ const ciReport = formatCIJson(extendedReport, {
1710
+ minHealth,
1711
+ hasTypecheckErrors,
1712
+ hasRuntimeErrors,
1713
+ hasStaleRefs
1714
+ });
1715
+ const jsonContent = JSON.stringify(ciReport, null, 2);
1637
1716
  let formatContent;
1638
1717
  switch (format) {
1639
1718
  case "json":
@@ -1656,13 +1735,9 @@ function handleNonTextOutput(options, deps) {
1656
1735
  cwd
1657
1736
  });
1658
1737
  }
1659
- const healthFailed = healthScore < minHealth;
1660
1738
  const apiSurfaceScore = doccov.apiSurface?.completeness ?? 100;
1661
1739
  const apiSurfaceFailed = minApiSurface !== undefined && apiSurfaceScore < minApiSurface;
1662
- const hasTypecheckErrors = typecheckErrors.length > 0;
1663
- const hasRuntimeErrors = runtimeErrors > 0;
1664
- const hasStaleRefs = staleRefs.length > 0;
1665
- return !(healthFailed || apiSurfaceFailed || hasTypecheckErrors || hasRuntimeErrors || hasStaleRefs);
1740
+ return ciReport.success && !apiSurfaceFailed;
1666
1741
  }
1667
1742
  function displayApiSurfaceOutput(doccov, deps) {
1668
1743
  const { log } = deps;
@@ -1710,6 +1785,100 @@ function displayApiSurfaceOutput(doccov, deps) {
1710
1785
  log(colors.success(`${sym.success} All referenced types are exported`));
1711
1786
  }
1712
1787
  }
1788
+ function displayBatchTextOutput(options, deps) {
1789
+ const { batchResult, minHealth, verbose } = options;
1790
+ const { log } = deps;
1791
+ const { packages, aggregate } = batchResult;
1792
+ const sym = getSymbols(supportsUnicode());
1793
+ log("");
1794
+ log(colors.bold(`Documentation Coverage Report (${packages.length} packages)`));
1795
+ log("");
1796
+ log("| Package | Health | Exports | Drift |");
1797
+ log("|---------|--------|---------|-------|");
1798
+ for (const pkg of packages) {
1799
+ const healthColor = pkg.health >= 80 ? colors.success : pkg.health >= 60 ? colors.warning : colors.error;
1800
+ log(`| ${pkg.name} | ${healthColor(`${pkg.health}%`)} | ${pkg.totalExports} | ${pkg.driftCount} |`);
1801
+ }
1802
+ const totalHealthColor = aggregate.health >= 80 ? colors.success : aggregate.health >= 60 ? colors.warning : colors.error;
1803
+ log(`| ${colors.bold("Total")} | ${totalHealthColor(colors.bold(`${aggregate.health}%`))} | ${colors.bold(String(aggregate.totalExports))} | ${colors.bold(String(aggregate.driftCount))} |`);
1804
+ log("");
1805
+ const passed = aggregate.health >= minHealth;
1806
+ if (passed) {
1807
+ log(colors.success(`${sym.success} Check passed (health ${aggregate.health}% >= ${minHealth}%)`));
1808
+ } else {
1809
+ log(colors.error(`${sym.error} Health ${aggregate.health}% below minimum ${minHealth}%`));
1810
+ }
1811
+ if (verbose) {
1812
+ log("");
1813
+ for (const pkg of packages) {
1814
+ const health = pkg.doccov.summary.health;
1815
+ if (health) {
1816
+ log(colors.bold(`${pkg.name}:`));
1817
+ displayHealthVerbose(health, log);
1818
+ log("");
1819
+ }
1820
+ }
1821
+ }
1822
+ return passed;
1823
+ }
1824
+ function handleBatchNonTextOutput(options, deps) {
1825
+ const { format, batchResult, minHealth, limit, stdout, outputPath, cwd } = options;
1826
+ const { log } = deps;
1827
+ const healthPassed = batchResult.aggregate.health >= minHealth;
1828
+ const ciBatchReport = {
1829
+ success: healthPassed,
1830
+ health: batchResult.aggregate.health,
1831
+ thresholds: {
1832
+ health: {
1833
+ min: minHealth,
1834
+ actual: batchResult.aggregate.health,
1835
+ passed: healthPassed
1836
+ }
1837
+ },
1838
+ drift: {
1839
+ total: batchResult.aggregate.driftCount
1840
+ },
1841
+ exports: {
1842
+ total: batchResult.aggregate.totalExports,
1843
+ documented: batchResult.aggregate.documented
1844
+ },
1845
+ exitCode: healthPassed ? 0 : 1,
1846
+ packages: batchResult.packages.map((p) => ({
1847
+ name: p.name,
1848
+ version: p.version,
1849
+ entryPath: p.entryPath,
1850
+ totalExports: p.totalExports,
1851
+ documented: p.documented,
1852
+ health: p.health,
1853
+ driftCount: p.driftCount,
1854
+ coverageScore: p.coverageScore
1855
+ })),
1856
+ aggregate: batchResult.aggregate
1857
+ };
1858
+ let formatContent;
1859
+ switch (format) {
1860
+ case "json":
1861
+ formatContent = JSON.stringify(ciBatchReport, null, 2);
1862
+ break;
1863
+ case "markdown":
1864
+ formatContent = renderBatchMarkdown(batchResult, { limit });
1865
+ break;
1866
+ default:
1867
+ throw new Error(`Unknown format: ${format}`);
1868
+ }
1869
+ if (stdout) {
1870
+ log(formatContent);
1871
+ } else {
1872
+ writeReports({
1873
+ format,
1874
+ formatContent,
1875
+ jsonContent: JSON.stringify(ciBatchReport, null, 2),
1876
+ outputPath,
1877
+ cwd
1878
+ });
1879
+ }
1880
+ return healthPassed;
1881
+ }
1713
1882
 
1714
1883
  // src/commands/check/validation.ts
1715
1884
  import { validateExamples } from "@doccov/sdk";
@@ -3718,6 +3887,48 @@ async function validateMarkdownDocs(options) {
3718
3887
  return staleRefs;
3719
3888
  }
3720
3889
 
3890
+ // src/utils/signal-handler.ts
3891
+ import chalk6 from "chalk";
3892
+ var state = {
3893
+ handlers: [],
3894
+ registered: false
3895
+ };
3896
+ function registerSignalHandlers() {
3897
+ if (state.registered)
3898
+ return;
3899
+ const handler = (signal) => {
3900
+ console.error(chalk6.yellow(`
3901
+ ⚠️ Received ${signal}, shutting down...`));
3902
+ if (state.incrementalAnalyzer && state.onPartialResults) {
3903
+ try {
3904
+ const partial = state.incrementalAnalyzer.getPartialResultsSync();
3905
+ if (partial.results.length > 0) {
3906
+ console.error(chalk6.yellow(`
3907
+ Partial results available: ${partial.results.length} exports analyzed`));
3908
+ state.onPartialResults(partial);
3909
+ }
3910
+ } catch {}
3911
+ }
3912
+ for (const h of state.handlers) {
3913
+ try {
3914
+ h();
3915
+ } catch {}
3916
+ }
3917
+ process.exit(1);
3918
+ };
3919
+ process.on("SIGINT", () => handler("SIGINT"));
3920
+ process.on("SIGTERM", () => handler("SIGTERM"));
3921
+ state.registered = true;
3922
+ }
3923
+ function setIncrementalAnalyzer(analyzer, onPartialResults) {
3924
+ state.incrementalAnalyzer = analyzer;
3925
+ state.onPartialResults = onPartialResults;
3926
+ }
3927
+ function clearIncrementalAnalyzer() {
3928
+ state.incrementalAnalyzer = undefined;
3929
+ state.onPartialResults = undefined;
3930
+ }
3931
+
3721
3932
  // src/commands/check/index.ts
3722
3933
  var defaultDependencies = {
3723
3934
  createDocCov: (options) => new DocCov(options),
@@ -3729,16 +3940,24 @@ function registerCheckCommand(program, dependencies = {}) {
3729
3940
  ...defaultDependencies,
3730
3941
  ...dependencies
3731
3942
  };
3732
- program.command("check [entry]").description("Check documentation coverage and output reports").option("--cwd <dir>", "Working directory", process.cwd()).option("--package <name>", "Target package name (for monorepos)").option("--min-health <percentage>", "Minimum health score (0-100)", (value) => Number(value)).option("--examples [mode]", "Example validation: presence, typecheck, run (comma-separated). Bare flag runs all.").option("--skip-resolve", "Skip external type resolution from node_modules").option("--docs <glob>", "Glob pattern for markdown docs to check for stale refs", collect, []).option("--fix", "Auto-fix drift issues").option("--preview", "Preview fixes with diff output (implies --fix)").option("--format <format>", "Output format: text, json, markdown", "text").option("-o, --output <file>", "Custom output path (overrides default .doccov/ path)").option("--stdout", "Output to stdout instead of writing to .doccov/").option("--limit <n>", "Max exports to show in report tables", "20").option("--max-type-depth <number>", "Maximum depth for type conversion (default: 20)", (value) => {
3943
+ program.command("check [targets...]").description("Check documentation coverage and output reports").option("--cwd <dir>", "Working directory", process.cwd()).option("--package <name>", "Target package name (for monorepos)").option("--min-health <percentage>", "Minimum health score (0-100)", (value) => Number(value)).option("--examples [mode]", "Example validation: presence, typecheck, run (comma-separated). Bare flag runs all.").option("--skip-resolve", "Skip external type resolution from node_modules").option("--docs <glob>", "Glob pattern for markdown docs to check for stale refs", collect, []).option("--fix", "Auto-fix drift issues").option("--preview", "Preview fixes with diff output (implies --fix)").option("--format <format>", "Output format: text, json, markdown", "text").option("-o, --output <file>", "Custom output path (overrides default .doccov/ path)").option("--stdout", "Output to stdout instead of writing to .doccov/").option("--limit <n>", "Max exports to show in report tables", "20").option("--max-type-depth <number>", "Maximum depth for type conversion (default: 20)", (value) => {
3733
3944
  const n = parseInt(value, 10);
3734
3945
  if (Number.isNaN(n) || n < 1)
3735
3946
  throw new Error("--max-type-depth must be a positive integer");
3736
3947
  return n;
3737
- }).option("--no-cache", "Bypass spec cache and force regeneration").option("--visibility <tags>", "Filter by release stage: public,beta,alpha,internal (comma-separated)").option("--api-surface", "Show only API surface / forgotten exports info").option("-v, --verbose", "Show detailed output including forgotten exports").action(async (entry, options) => {
3948
+ }).option("--no-cache", "Bypass spec cache and force regeneration").option("--visibility <tags>", "Filter by release stage: public,beta,alpha,internal (comma-separated)").option("--api-surface", "Show only API surface / forgotten exports info").option("-v, --verbose", "Show detailed output including forgotten exports").action(async (targets, options) => {
3949
+ registerSignalHandlers();
3738
3950
  try {
3739
3951
  const spin = spinner("Analyzing...");
3952
+ const resolvedTargets = await expandGlobTargets(targets, options.cwd);
3953
+ const isBatchMode = resolvedTargets.length > 1;
3740
3954
  let validations = parseExamplesFlag(options.examples);
3741
3955
  let hasExamples = validations.length > 0;
3956
+ if (isBatchMode) {
3957
+ await runBatchAnalysis(resolvedTargets, options, { createDocCov, log, error, spin });
3958
+ return;
3959
+ }
3960
+ const entry = resolvedTargets[0];
3742
3961
  const fileSystem = new NodeFileSystem(options.cwd);
3743
3962
  const resolved = await resolveTarget(fileSystem, {
3744
3963
  cwd: options.cwd,
@@ -3771,7 +3990,7 @@ function registerCheckCommand(program, dependencies = {}) {
3771
3990
  };
3772
3991
  const resolvedFilters = mergeFilterOptions(config, cliFilters);
3773
3992
  if (resolvedFilters.visibility) {
3774
- log(chalk6.dim(`Filtering by visibility: ${resolvedFilters.visibility.join(", ")}`));
3993
+ log(chalk7.dim(`Filtering by visibility: ${resolvedFilters.visibility.join(", ")}`));
3775
3994
  }
3776
3995
  const resolveExternalTypes = !options.skipResolve;
3777
3996
  const analyzer = createDocCov({
@@ -3787,13 +4006,51 @@ function registerCheckCommand(program, dependencies = {}) {
3787
4006
  throw new Error("Failed to analyze documentation coverage.");
3788
4007
  }
3789
4008
  const openpkg = specResult.spec;
3790
- const doccov = buildDocCovSpec({
4009
+ spin.update("Building coverage spec...");
4010
+ let entryExportNames;
4011
+ const entryResult = await findEntryPointForFile(fileSystem, entryFile);
4012
+ if (entryResult) {
4013
+ const pkgEntryPath = path8.resolve(entryResult.packagePath, entryResult.entryPoint.path);
4014
+ const isSubFile = path8.resolve(entryFile) !== pkgEntryPath;
4015
+ if (isSubFile) {
4016
+ const entryAnalyzer = createDocCov({
4017
+ resolveExternalTypes: false,
4018
+ useCache: options.cache !== false,
4019
+ cwd: entryResult.packagePath
4020
+ });
4021
+ const entrySpec = await entryAnalyzer.analyzeFileWithDiagnostics(pkgEntryPath);
4022
+ if (entrySpec?.spec?.exports) {
4023
+ entryExportNames = entrySpec.spec.exports.map((e) => e.name);
4024
+ }
4025
+ }
4026
+ }
4027
+ const incrementalAnalyzer = new IncrementalAnalyzer;
4028
+ setIncrementalAnalyzer(incrementalAnalyzer, (partial) => {
4029
+ log(chalk7.yellow(`
4030
+ ⚠️ Partial results (${partial.results.length} exports):`));
4031
+ for (const r of partial.results.slice(-5)) {
4032
+ log(chalk7.dim(` - ${r.name}: ${r.coverageScore}%`));
4033
+ }
4034
+ if (partial.results.length > 5) {
4035
+ log(chalk7.dim(` ... and ${partial.results.length - 5} more`));
4036
+ }
4037
+ });
4038
+ const doccov = await buildDocCovSpec({
3791
4039
  openpkg,
3792
4040
  openpkgPath: entryFile,
3793
4041
  packagePath: targetDir,
3794
4042
  forgottenExports: specResult.forgottenExports,
3795
- apiSurfaceIgnore
4043
+ apiSurfaceIgnore,
4044
+ entryExportNames,
4045
+ onProgress: (current, total, item) => {
4046
+ spin.setDetail(`${current}/${total}: ${item}`);
4047
+ },
4048
+ onExportAnalyzed: async (id, name, analysis) => {
4049
+ await incrementalAnalyzer.writeExportAnalysis(id, name, analysis);
4050
+ }
3796
4051
  });
4052
+ clearIncrementalAnalyzer();
4053
+ await incrementalAnalyzer.cleanup();
3797
4054
  const format = options.format ?? "text";
3798
4055
  const specWarnings = specResult.diagnostics.filter((d) => d.severity === "warning");
3799
4056
  const specInfos = specResult.diagnostics.filter((d) => d.severity === "info" && !d.code?.startsWith("EXTERNAL_TYPE"));
@@ -3879,14 +4136,103 @@ function registerCheckCommand(program, dependencies = {}) {
3879
4136
  process.exit(1);
3880
4137
  }
3881
4138
  } catch (commandError) {
3882
- error(chalk6.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
4139
+ error(chalk7.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
3883
4140
  process.exit(1);
3884
4141
  }
3885
4142
  });
3886
4143
  }
4144
+ async function expandGlobTargets(targets, cwd) {
4145
+ if (targets.length === 0) {
4146
+ return [];
4147
+ }
4148
+ const expanded = [];
4149
+ for (const target of targets) {
4150
+ if (target.includes("*")) {
4151
+ const matches = await glob2(target, { cwd, nodir: true });
4152
+ expanded.push(...matches.map((m) => path8.resolve(cwd, m)));
4153
+ } else {
4154
+ expanded.push(path8.resolve(cwd, target));
4155
+ }
4156
+ }
4157
+ return expanded;
4158
+ }
4159
+ async function runBatchAnalysis(targets, options, deps) {
4160
+ const { createDocCov, log, error, spin } = deps;
4161
+ const fileSystem = new NodeFileSystem(options.cwd);
4162
+ const packageResults = [];
4163
+ for (let i = 0;i < targets.length; i++) {
4164
+ const target = targets[i];
4165
+ spin.update(`Analyzing ${i + 1}/${targets.length}: ${path8.basename(target)}...`);
4166
+ try {
4167
+ const resolved = await resolveTarget(fileSystem, {
4168
+ cwd: options.cwd,
4169
+ entry: target
4170
+ });
4171
+ const { targetDir, entryFile } = resolved;
4172
+ const config2 = await loadDocCovConfig(targetDir);
4173
+ const resolveExternalTypes = !options.skipResolve;
4174
+ const analyzer = createDocCov({
4175
+ resolveExternalTypes,
4176
+ maxDepth: options.maxTypeDepth,
4177
+ useCache: options.cache !== false,
4178
+ cwd: targetDir
4179
+ });
4180
+ const specResult = await analyzer.analyzeFileWithDiagnostics(entryFile);
4181
+ if (!specResult) {
4182
+ log(chalk7.yellow(` Skipping ${target}: analysis failed`));
4183
+ continue;
4184
+ }
4185
+ const openpkg = specResult.spec;
4186
+ const doccov = await buildDocCovSpec({
4187
+ openpkg,
4188
+ openpkgPath: entryFile,
4189
+ packagePath: targetDir,
4190
+ forgottenExports: specResult.forgottenExports
4191
+ });
4192
+ packageResults.push(createPackageResult(openpkg, doccov, entryFile));
4193
+ } catch (err) {
4194
+ log(chalk7.yellow(` Skipping ${target}: ${err instanceof Error ? err.message : err}`));
4195
+ }
4196
+ }
4197
+ if (packageResults.length === 0) {
4198
+ spin.fail("No packages analyzed successfully");
4199
+ process.exit(1);
4200
+ }
4201
+ spin.success(`Analyzed ${packageResults.length} package(s)`);
4202
+ const batchResult = aggregateResults(packageResults);
4203
+ const firstTargetDir = path8.dirname(targets[0]);
4204
+ const config = await loadDocCovConfig(firstTargetDir);
4205
+ const DEFAULT_MIN_HEALTH = 80;
4206
+ const minHealthRaw = options.minHealth ?? config?.check?.minHealth ?? DEFAULT_MIN_HEALTH;
4207
+ const minHealth = clampPercentage(minHealthRaw);
4208
+ const format = options.format ?? "text";
4209
+ if (format !== "text") {
4210
+ const passed2 = handleBatchNonTextOutput({
4211
+ format,
4212
+ batchResult,
4213
+ minHealth,
4214
+ limit: parseInt(options.limit, 10) || 20,
4215
+ stdout: options.stdout,
4216
+ outputPath: options.output,
4217
+ cwd: options.cwd
4218
+ }, { log });
4219
+ if (!passed2) {
4220
+ process.exit(1);
4221
+ }
4222
+ return;
4223
+ }
4224
+ const passed = displayBatchTextOutput({
4225
+ batchResult,
4226
+ minHealth,
4227
+ verbose: options.verbose ?? false
4228
+ }, { log });
4229
+ if (!passed) {
4230
+ process.exit(1);
4231
+ }
4232
+ }
3887
4233
  // src/commands/diff.ts
3888
4234
  import * as fs5 from "node:fs";
3889
- import * as path8 from "node:path";
4235
+ import * as path9 from "node:path";
3890
4236
  import {
3891
4237
  diffSpecWithDocs,
3892
4238
  ensureSpecCoverage,
@@ -3897,8 +4243,8 @@ import {
3897
4243
  parseMarkdownFiles as parseMarkdownFiles2
3898
4244
  } from "@doccov/sdk";
3899
4245
  import { calculateNextVersion, recommendSemverBump } from "@openpkg-ts/spec";
3900
- import chalk7 from "chalk";
3901
- import { glob as glob2 } from "glob";
4246
+ import chalk8 from "chalk";
4247
+ import { glob as glob3 } from "glob";
3902
4248
  var defaultDependencies2 = {
3903
4249
  readFileSync: fs5.readFileSync,
3904
4250
  log: console.log,
@@ -3939,7 +4285,7 @@ function registerDiffCommand(program, dependencies = {}) {
3939
4285
  const baseHash = hashString(JSON.stringify(baseSpec));
3940
4286
  const headHash = hashString(JSON.stringify(headSpec));
3941
4287
  const cacheEnabled = options.cache !== false;
3942
- const cachedReportPath = path8.resolve(options.cwd, getDiffReportPath(baseHash, headHash, "json"));
4288
+ const cachedReportPath = path9.resolve(options.cwd, getDiffReportPath(baseHash, headHash, "json"));
3943
4289
  let diff;
3944
4290
  let fromCache = false;
3945
4291
  if (cacheEnabled && fs5.existsSync(cachedReportPath)) {
@@ -3972,9 +4318,9 @@ function registerDiffCommand(program, dependencies = {}) {
3972
4318
  }, null, 2));
3973
4319
  } else {
3974
4320
  log("");
3975
- log(chalk7.bold("Semver Recommendation"));
4321
+ log(chalk8.bold("Semver Recommendation"));
3976
4322
  log(` Current version: ${currentVersion}`);
3977
- log(` Recommended: ${chalk7.cyan(nextVersion)} (${chalk7.yellow(recommendation.bump.toUpperCase())})`);
4323
+ log(` Recommended: ${chalk8.cyan(nextVersion)} (${chalk8.yellow(recommendation.bump.toUpperCase())})`);
3978
4324
  log(` Reason: ${recommendation.reason}`);
3979
4325
  }
3980
4326
  return;
@@ -3982,8 +4328,8 @@ function registerDiffCommand(program, dependencies = {}) {
3982
4328
  const format = options.format ?? "text";
3983
4329
  const limit = parseInt(options.limit, 10) || 10;
3984
4330
  const checks = getStrictChecks(options.strict);
3985
- const baseName = path8.basename(baseFile);
3986
- const headName = path8.basename(headFile);
4331
+ const baseName = path9.basename(baseFile);
4332
+ const headName = path9.basename(headFile);
3987
4333
  const reportData = {
3988
4334
  baseName,
3989
4335
  headName,
@@ -4004,8 +4350,8 @@ function registerDiffCommand(program, dependencies = {}) {
4004
4350
  silent: true
4005
4351
  });
4006
4352
  }
4007
- const cacheNote = fromCache ? chalk7.cyan(" (cached)") : "";
4008
- log(chalk7.dim(`Report: ${jsonPath}`) + cacheNote);
4353
+ const cacheNote = fromCache ? chalk8.cyan(" (cached)") : "";
4354
+ log(chalk8.dim(`Report: ${jsonPath}`) + cacheNote);
4009
4355
  }
4010
4356
  break;
4011
4357
  case "json": {
@@ -4063,18 +4409,18 @@ function registerDiffCommand(program, dependencies = {}) {
4063
4409
  checks
4064
4410
  });
4065
4411
  if (failures.length > 0) {
4066
- log(chalk7.red(`
4412
+ log(chalk8.red(`
4067
4413
  ✗ Check failed`));
4068
4414
  for (const f of failures) {
4069
- log(chalk7.red(` - ${f}`));
4415
+ log(chalk8.red(` - ${f}`));
4070
4416
  }
4071
4417
  process.exitCode = 1;
4072
4418
  } else if (options.strict || minCoverage !== undefined || maxDrift !== undefined) {
4073
- log(chalk7.green(`
4419
+ log(chalk8.green(`
4074
4420
  ✓ All checks passed`));
4075
4421
  }
4076
4422
  } catch (commandError) {
4077
- error(chalk7.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
4423
+ error(chalk8.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
4078
4424
  process.exitCode = 1;
4079
4425
  }
4080
4426
  });
@@ -4085,7 +4431,7 @@ function collect2(value, previous) {
4085
4431
  async function loadMarkdownFiles2(patterns) {
4086
4432
  const files = [];
4087
4433
  for (const pattern of patterns) {
4088
- const matches = await glob2(pattern, { nodir: true });
4434
+ const matches = await glob3(pattern, { nodir: true });
4089
4435
  for (const filePath of matches) {
4090
4436
  try {
4091
4437
  const content = fs5.readFileSync(filePath, "utf-8");
@@ -4101,7 +4447,7 @@ async function generateDiff(baseSpec, headSpec, options, config, log) {
4101
4447
  if (!docsPatterns || docsPatterns.length === 0) {
4102
4448
  if (config?.docs?.include) {
4103
4449
  docsPatterns = config.docs.include;
4104
- log(chalk7.gray(`Using docs patterns from config: ${docsPatterns.join(", ")}`));
4450
+ log(chalk8.gray(`Using docs patterns from config: ${docsPatterns.join(", ")}`));
4105
4451
  }
4106
4452
  }
4107
4453
  if (docsPatterns && docsPatterns.length > 0) {
@@ -4110,7 +4456,7 @@ async function generateDiff(baseSpec, headSpec, options, config, log) {
4110
4456
  return diffSpecWithDocs(baseSpec, headSpec, { markdownFiles });
4111
4457
  }
4112
4458
  function loadSpec(filePath, readFileSync4) {
4113
- const resolvedPath = path8.resolve(filePath);
4459
+ const resolvedPath = path9.resolve(filePath);
4114
4460
  if (!fs5.existsSync(resolvedPath)) {
4115
4461
  throw new Error(`File not found: ${filePath}`);
4116
4462
  }
@@ -4124,37 +4470,37 @@ function loadSpec(filePath, readFileSync4) {
4124
4470
  }
4125
4471
  function printSummary(diff, baseName, headName, fromCache, log) {
4126
4472
  log("");
4127
- const cacheIndicator = fromCache ? chalk7.cyan(" (cached)") : "";
4128
- log(chalk7.bold(`Comparing: ${baseName} → ${headName}`) + cacheIndicator);
4473
+ const cacheIndicator = fromCache ? chalk8.cyan(" (cached)") : "";
4474
+ log(chalk8.bold(`Comparing: ${baseName} → ${headName}`) + cacheIndicator);
4129
4475
  log("─".repeat(40));
4130
4476
  log("");
4131
- const coverageColor = diff.coverageDelta > 0 ? chalk7.green : diff.coverageDelta < 0 ? chalk7.red : chalk7.gray;
4477
+ const coverageColor = diff.coverageDelta > 0 ? chalk8.green : diff.coverageDelta < 0 ? chalk8.red : chalk8.gray;
4132
4478
  const coverageSign = diff.coverageDelta > 0 ? "+" : "";
4133
4479
  log(` Coverage: ${diff.oldCoverage}% → ${diff.newCoverage}% ${coverageColor(`(${coverageSign}${diff.coverageDelta}%)`)}`);
4134
4480
  const breakingCount = diff.breaking.length;
4135
4481
  const highSeverity = diff.categorizedBreaking?.filter((c) => c.severity === "high").length ?? 0;
4136
4482
  if (breakingCount > 0) {
4137
- const severityNote = highSeverity > 0 ? chalk7.red(` (${highSeverity} high severity)`) : "";
4138
- log(` Breaking: ${chalk7.red(breakingCount)} changes${severityNote}`);
4483
+ const severityNote = highSeverity > 0 ? chalk8.red(` (${highSeverity} high severity)`) : "";
4484
+ log(` Breaking: ${chalk8.red(breakingCount)} changes${severityNote}`);
4139
4485
  } else {
4140
- log(` Breaking: ${chalk7.green("0")} changes`);
4486
+ log(` Breaking: ${chalk8.green("0")} changes`);
4141
4487
  }
4142
4488
  const newCount = diff.nonBreaking.length;
4143
4489
  const undocCount = diff.newUndocumented.length;
4144
4490
  if (newCount > 0) {
4145
- const undocNote = undocCount > 0 ? chalk7.yellow(` (${undocCount} undocumented)`) : "";
4146
- log(` New: ${chalk7.green(newCount)} exports${undocNote}`);
4491
+ const undocNote = undocCount > 0 ? chalk8.yellow(` (${undocCount} undocumented)`) : "";
4492
+ log(` New: ${chalk8.green(newCount)} exports${undocNote}`);
4147
4493
  }
4148
4494
  if (diff.driftIntroduced > 0 || diff.driftResolved > 0) {
4149
4495
  const parts = [];
4150
4496
  if (diff.driftIntroduced > 0)
4151
- parts.push(chalk7.red(`+${diff.driftIntroduced}`));
4497
+ parts.push(chalk8.red(`+${diff.driftIntroduced}`));
4152
4498
  if (diff.driftResolved > 0)
4153
- parts.push(chalk7.green(`-${diff.driftResolved}`));
4499
+ parts.push(chalk8.green(`-${diff.driftResolved}`));
4154
4500
  log(` Drift: ${parts.join(", ")}`);
4155
4501
  }
4156
4502
  const recommendation = recommendSemverBump(diff);
4157
- const bumpColor = recommendation.bump === "major" ? chalk7.red : recommendation.bump === "minor" ? chalk7.yellow : chalk7.green;
4503
+ const bumpColor = recommendation.bump === "major" ? chalk8.red : recommendation.bump === "minor" ? chalk8.yellow : chalk8.green;
4158
4504
  log(` Semver: ${bumpColor(recommendation.bump.toUpperCase())} (${recommendation.reason})`);
4159
4505
  log("");
4160
4506
  }
@@ -4236,8 +4582,8 @@ function printGitHubAnnotations(diff, log) {
4236
4582
 
4237
4583
  // src/commands/init.ts
4238
4584
  import * as fs6 from "node:fs";
4239
- import * as path9 from "node:path";
4240
- import chalk8 from "chalk";
4585
+ import * as path10 from "node:path";
4586
+ import chalk9 from "chalk";
4241
4587
  var defaultDependencies3 = {
4242
4588
  fileExists: fs6.existsSync,
4243
4589
  writeFileSync: fs6.writeFileSync,
@@ -4252,19 +4598,19 @@ function registerInitCommand(program, dependencies = {}) {
4252
4598
  ...dependencies
4253
4599
  };
4254
4600
  program.command("init").description("Initialize DocCov: config, GitHub Action, and badge").option("--cwd <dir>", "Working directory", process.cwd()).option("--skip-action", "Skip GitHub Action workflow creation").action((options) => {
4255
- const cwd = path9.resolve(options.cwd);
4601
+ const cwd = path10.resolve(options.cwd);
4256
4602
  const existing = findExistingConfig(cwd, fileExists2);
4257
4603
  if (existing) {
4258
- error(chalk8.red(`A DocCov config already exists at ${path9.relative(cwd, existing) || "./doccov.config.*"}.`));
4604
+ error(chalk9.red(`A DocCov config already exists at ${path10.relative(cwd, existing) || "./doccov.config.*"}.`));
4259
4605
  process.exitCode = 1;
4260
4606
  return;
4261
4607
  }
4262
4608
  const packageType = detectPackageType(cwd, fileExists2, readFileSync5);
4263
4609
  const targetFormat = packageType === "module" ? "ts" : "mts";
4264
4610
  const fileName = `doccov.config.${targetFormat}`;
4265
- const outputPath = path9.join(cwd, fileName);
4611
+ const outputPath = path10.join(cwd, fileName);
4266
4612
  if (fileExists2(outputPath)) {
4267
- error(chalk8.red(`Cannot create ${fileName}; file already exists.`));
4613
+ error(chalk9.red(`Cannot create ${fileName}; file already exists.`));
4268
4614
  process.exitCode = 1;
4269
4615
  return;
4270
4616
  }
@@ -4273,8 +4619,8 @@ function registerInitCommand(program, dependencies = {}) {
4273
4619
  writeFileSync4(outputPath, template, { encoding: "utf8" });
4274
4620
  log(colors.success(`${sym.success} Created ${fileName}`));
4275
4621
  if (!options.skipAction) {
4276
- const workflowDir = path9.join(cwd, ".github", "workflows");
4277
- const workflowPath = path9.join(workflowDir, "doccov.yml");
4622
+ const workflowDir = path10.join(cwd, ".github", "workflows");
4623
+ const workflowPath = path10.join(workflowDir, "doccov.yml");
4278
4624
  if (!fileExists2(workflowPath)) {
4279
4625
  mkdirSync4(workflowDir, { recursive: true });
4280
4626
  writeFileSync4(workflowPath, buildWorkflowTemplate(), { encoding: "utf8" });
@@ -4285,24 +4631,24 @@ function registerInitCommand(program, dependencies = {}) {
4285
4631
  }
4286
4632
  const repoInfo = detectRepoInfo(cwd, fileExists2, readFileSync5);
4287
4633
  log("");
4288
- log(chalk8.bold("Add this badge to your README:"));
4634
+ log(chalk9.bold("Add this badge to your README:"));
4289
4635
  log("");
4290
4636
  if (repoInfo) {
4291
- log(chalk8.cyan(`[![DocCov](https://doccov.dev/badge/${repoInfo.owner}/${repoInfo.repo})](https://doccov.dev/${repoInfo.owner}/${repoInfo.repo})`));
4637
+ log(chalk9.cyan(`[![DocCov](https://doccov.dev/badge/${repoInfo.owner}/${repoInfo.repo})](https://doccov.dev/${repoInfo.owner}/${repoInfo.repo})`));
4292
4638
  } else {
4293
- log(chalk8.cyan(`[![DocCov](https://doccov.dev/badge/OWNER/REPO)](https://doccov.dev/OWNER/REPO)`));
4294
- log(chalk8.dim(" Replace OWNER/REPO with your GitHub repo"));
4639
+ log(chalk9.cyan(`[![DocCov](https://doccov.dev/badge/OWNER/REPO)](https://doccov.dev/OWNER/REPO)`));
4640
+ log(chalk9.dim(" Replace OWNER/REPO with your GitHub repo"));
4295
4641
  }
4296
4642
  log("");
4297
- log(chalk8.dim("Run `doccov check` to verify your documentation coverage"));
4643
+ log(chalk9.dim("Run `doccov check` to verify your documentation coverage"));
4298
4644
  });
4299
4645
  }
4300
4646
  var findExistingConfig = (cwd, fileExists2) => {
4301
- let current = path9.resolve(cwd);
4302
- const { root } = path9.parse(current);
4647
+ let current = path10.resolve(cwd);
4648
+ const { root } = path10.parse(current);
4303
4649
  while (true) {
4304
4650
  for (const candidate of DOCCOV_CONFIG_FILENAMES) {
4305
- const candidatePath = path9.join(current, candidate);
4651
+ const candidatePath = path10.join(current, candidate);
4306
4652
  if (fileExists2(candidatePath)) {
4307
4653
  return candidatePath;
4308
4654
  }
@@ -4310,7 +4656,7 @@ var findExistingConfig = (cwd, fileExists2) => {
4310
4656
  if (current === root) {
4311
4657
  break;
4312
4658
  }
4313
- current = path9.dirname(current);
4659
+ current = path10.dirname(current);
4314
4660
  }
4315
4661
  return null;
4316
4662
  };
@@ -4332,17 +4678,17 @@ var detectPackageType = (cwd, fileExists2, readFileSync5) => {
4332
4678
  return;
4333
4679
  };
4334
4680
  var findNearestPackageJson = (cwd, fileExists2) => {
4335
- let current = path9.resolve(cwd);
4336
- const { root } = path9.parse(current);
4681
+ let current = path10.resolve(cwd);
4682
+ const { root } = path10.parse(current);
4337
4683
  while (true) {
4338
- const candidate = path9.join(current, "package.json");
4684
+ const candidate = path10.join(current, "package.json");
4339
4685
  if (fileExists2(candidate)) {
4340
4686
  return candidate;
4341
4687
  }
4342
4688
  if (current === root) {
4343
4689
  break;
4344
4690
  }
4345
- current = path9.dirname(current);
4691
+ current = path10.dirname(current);
4346
4692
  }
4347
4693
  return null;
4348
4694
  };
@@ -4404,7 +4750,7 @@ var detectRepoInfo = (cwd, fileExists2, readFileSync5) => {
4404
4750
  }
4405
4751
  } catch {}
4406
4752
  }
4407
- const gitConfigPath = path9.join(cwd, ".git", "config");
4753
+ const gitConfigPath = path10.join(cwd, ".git", "config");
4408
4754
  if (fileExists2(gitConfigPath)) {
4409
4755
  try {
4410
4756
  const config = readFileSync5(gitConfigPath, "utf8");
@@ -4419,7 +4765,7 @@ var detectRepoInfo = (cwd, fileExists2, readFileSync5) => {
4419
4765
 
4420
4766
  // src/commands/spec.ts
4421
4767
  import * as fs7 from "node:fs";
4422
- import * as path10 from "node:path";
4768
+ import * as path11 from "node:path";
4423
4769
  import {
4424
4770
  buildDocCovSpec as buildDocCovSpec2,
4425
4771
  DocCov as DocCov2,
@@ -4434,7 +4780,7 @@ import {
4434
4780
  normalize,
4435
4781
  validateSpec
4436
4782
  } from "@openpkg-ts/spec";
4437
- import chalk9 from "chalk";
4783
+ import chalk10 from "chalk";
4438
4784
  var defaultDependencies4 = {
4439
4785
  createDocCov: (options) => new DocCov2(options),
4440
4786
  writeFileSync: fs7.writeFileSync,
@@ -4446,8 +4792,8 @@ function getArrayLength(value) {
4446
4792
  }
4447
4793
  function formatDiagnosticOutput(prefix2, diagnostic, baseDir) {
4448
4794
  const location = diagnostic.location;
4449
- const relativePath = location?.file ? path10.relative(baseDir, location.file) || location.file : undefined;
4450
- const locationText = location && relativePath ? chalk9.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
4795
+ const relativePath = location?.file ? path11.relative(baseDir, location.file) || location.file : undefined;
4796
+ const locationText = location && relativePath ? chalk10.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
4451
4797
  const locationPrefix = locationText ? `${locationText} ` : "";
4452
4798
  return `${prefix2} ${locationPrefix}${diagnostic.message}`;
4453
4799
  }
@@ -4471,7 +4817,7 @@ function registerSpecCommand(program, dependencies = {}) {
4471
4817
  config = await loadDocCovConfig(targetDir);
4472
4818
  } catch (configError) {
4473
4819
  spin.fail("Failed to load config");
4474
- error(chalk9.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
4820
+ error(chalk10.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
4475
4821
  process.exit(1);
4476
4822
  }
4477
4823
  const cliFilters = {
@@ -4504,26 +4850,30 @@ function registerSpecCommand(program, dependencies = {}) {
4504
4850
  const validation = validateSpec(normalized);
4505
4851
  if (!validation.ok) {
4506
4852
  spin.fail("Validation failed");
4507
- error(chalk9.red("Spec failed schema validation"));
4853
+ error(chalk10.red("Spec failed schema validation"));
4508
4854
  for (const err of validation.errors) {
4509
- error(chalk9.red(`schema: ${err.instancePath || "/"} ${err.message}`));
4855
+ error(chalk10.red(`schema: ${err.instancePath || "/"} ${err.message}`));
4510
4856
  }
4511
4857
  process.exit(1);
4512
4858
  }
4513
4859
  let doccovSpec = null;
4514
4860
  if (!options.openpkgOnly) {
4515
- doccovSpec = buildDocCovSpec2({
4861
+ spin.update("Building coverage spec...");
4862
+ doccovSpec = await buildDocCovSpec2({
4516
4863
  openpkgPath: "openpkg.json",
4517
4864
  openpkg: normalized,
4518
4865
  packagePath: targetDir,
4519
- forgottenExports: result.forgottenExports
4866
+ forgottenExports: result.forgottenExports,
4867
+ onProgress: (current, total, item) => {
4868
+ spin.setDetail(`${current}/${total}: ${item}`);
4869
+ }
4520
4870
  });
4521
4871
  const doccovValidation = validateDocCovSpec(doccovSpec);
4522
4872
  if (!doccovValidation.ok) {
4523
4873
  spin.fail("DocCov validation failed");
4524
- error(chalk9.red("DocCov spec failed schema validation"));
4874
+ error(chalk10.red("DocCov spec failed schema validation"));
4525
4875
  for (const err of doccovValidation.errors) {
4526
- error(chalk9.red(`doccov: ${err.instancePath || "/"} ${err.message}`));
4876
+ error(chalk10.red(`doccov: ${err.instancePath || "/"} ${err.message}`));
4527
4877
  }
4528
4878
  process.exit(1);
4529
4879
  }
@@ -4533,7 +4883,7 @@ function registerSpecCommand(program, dependencies = {}) {
4533
4883
  if (resolved.packageInfo) {
4534
4884
  packageName = resolved.packageInfo.name;
4535
4885
  } else {
4536
- const pkgJsonPath = path10.join(targetDir, "package.json");
4886
+ const pkgJsonPath = path11.join(targetDir, "package.json");
4537
4887
  try {
4538
4888
  const pkgJson = JSON.parse(fs7.readFileSync(pkgJsonPath, "utf-8"));
4539
4889
  packageName = pkgJson.name ?? "unknown";
@@ -4541,28 +4891,28 @@ function registerSpecCommand(program, dependencies = {}) {
4541
4891
  packageName = "unknown";
4542
4892
  }
4543
4893
  }
4544
- const baseOutputDir = options.output === ".doccov" ? getDoccovDir3(options.cwd) : path10.resolve(options.cwd, options.output);
4545
- const outputDir = path10.join(baseOutputDir, packageName);
4894
+ const baseOutputDir = options.output === ".doccov" ? getDoccovDir3(options.cwd) : path11.resolve(options.cwd, options.output);
4895
+ const outputDir = path11.join(baseOutputDir, packageName);
4546
4896
  fs7.mkdirSync(outputDir, { recursive: true });
4547
4897
  const displayPath = `${options.output}/${packageName}`;
4548
4898
  if (format === "api-surface") {
4549
4899
  const apiSurface = renderApiSurface(normalized);
4550
- const apiSurfacePath = path10.join(outputDir, "api-surface.txt");
4900
+ const apiSurfacePath = path11.join(outputDir, "api-surface.txt");
4551
4901
  writeFileSync5(apiSurfacePath, apiSurface);
4552
4902
  spin.success(`Generated ${displayPath}/ (API surface)`);
4553
4903
  } else {
4554
- const openpkgPath = path10.join(outputDir, "openpkg.json");
4904
+ const openpkgPath = path11.join(outputDir, "openpkg.json");
4555
4905
  writeFileSync5(openpkgPath, JSON.stringify(normalized, null, 2));
4556
4906
  if (doccovSpec) {
4557
- const doccovPath = path10.join(outputDir, "doccov.json");
4907
+ const doccovPath = path11.join(outputDir, "doccov.json");
4558
4908
  writeFileSync5(doccovPath, JSON.stringify(doccovSpec, null, 2));
4559
4909
  spin.success(`Generated ${displayPath}/`);
4560
- log(chalk9.gray(` openpkg.json: ${getArrayLength(normalized.exports)} exports`));
4561
- log(chalk9.gray(` doccov.json: ${doccovSpec.summary.score}% coverage, ${doccovSpec.summary.drift.total} drift issues`));
4910
+ log(chalk10.gray(` openpkg.json: ${getArrayLength(normalized.exports)} exports`));
4911
+ log(chalk10.gray(` doccov.json: ${doccovSpec.summary.score}% coverage, ${doccovSpec.summary.drift.total} drift issues`));
4562
4912
  } else {
4563
4913
  spin.success(`Generated ${displayPath}/openpkg.json`);
4564
- log(chalk9.gray(` ${getArrayLength(normalized.exports)} exports`));
4565
- log(chalk9.gray(` ${getArrayLength(normalized.types)} types`));
4914
+ log(chalk10.gray(` ${getArrayLength(normalized.exports)} exports`));
4915
+ log(chalk10.gray(` ${getArrayLength(normalized.types)} types`));
4566
4916
  }
4567
4917
  }
4568
4918
  const isFullGenerationInfo = (gen) => {
@@ -4574,62 +4924,62 @@ function registerSpecCommand(program, dependencies = {}) {
4574
4924
  const pm = await detectPackageManager(fileSystem);
4575
4925
  const buildCmd = pm.name === "npm" ? "npm run build" : `${pm.name} run build`;
4576
4926
  log("");
4577
- log(chalk9.yellow("⚠ Runtime extraction requested but no schemas extracted."));
4578
- log(chalk9.yellow(` Ensure project is built (${buildCmd}) and dist/ exists.`));
4927
+ log(chalk10.yellow("⚠ Runtime extraction requested but no schemas extracted."));
4928
+ log(chalk10.yellow(` Ensure project is built (${buildCmd}) and dist/ exists.`));
4579
4929
  }
4580
4930
  if (options.verbose && fullGen) {
4581
4931
  log("");
4582
- log(chalk9.bold("Generation Info"));
4583
- log(chalk9.gray(` Timestamp: ${fullGen.timestamp}`));
4584
- log(chalk9.gray(` Generator: ${fullGen.generator.name}@${fullGen.generator.version}`));
4585
- log(chalk9.gray(` Entry point: ${fullGen.analysis.entryPoint}`));
4586
- log(chalk9.gray(` Detected via: ${fullGen.analysis.entryPointSource}`));
4587
- log(chalk9.gray(` Declaration only: ${fullGen.analysis.isDeclarationOnly ? "yes" : "no"}`));
4588
- log(chalk9.gray(` External types: ${fullGen.analysis.resolvedExternalTypes ? "resolved" : "skipped"}`));
4932
+ log(chalk10.bold("Generation Info"));
4933
+ log(chalk10.gray(` Timestamp: ${fullGen.timestamp}`));
4934
+ log(chalk10.gray(` Generator: ${fullGen.generator.name}@${fullGen.generator.version}`));
4935
+ log(chalk10.gray(` Entry point: ${fullGen.analysis.entryPoint}`));
4936
+ log(chalk10.gray(` Detected via: ${fullGen.analysis.entryPointSource}`));
4937
+ log(chalk10.gray(` Declaration only: ${fullGen.analysis.isDeclarationOnly ? "yes" : "no"}`));
4938
+ log(chalk10.gray(` External types: ${fullGen.analysis.resolvedExternalTypes ? "resolved" : "skipped"}`));
4589
4939
  if (fullGen.analysis.maxTypeDepth) {
4590
- log(chalk9.gray(` Max type depth: ${fullGen.analysis.maxTypeDepth}`));
4940
+ log(chalk10.gray(` Max type depth: ${fullGen.analysis.maxTypeDepth}`));
4591
4941
  }
4592
4942
  if (fullGen.analysis.schemaExtraction) {
4593
4943
  const se = fullGen.analysis.schemaExtraction;
4594
- log(chalk9.gray(` Schema extraction: ${se.method}`));
4944
+ log(chalk10.gray(` Schema extraction: ${se.method}`));
4595
4945
  if (se.runtimeCount) {
4596
- log(chalk9.gray(` Runtime schemas: ${se.runtimeCount} (${se.vendors?.join(", ")})`));
4946
+ log(chalk10.gray(` Runtime schemas: ${se.runtimeCount} (${se.vendors?.join(", ")})`));
4597
4947
  }
4598
4948
  }
4599
4949
  log("");
4600
- log(chalk9.bold("Environment"));
4601
- log(chalk9.gray(` node_modules: ${fullGen.environment.hasNodeModules ? "found" : "not found"}`));
4950
+ log(chalk10.bold("Environment"));
4951
+ log(chalk10.gray(` node_modules: ${fullGen.environment.hasNodeModules ? "found" : "not found"}`));
4602
4952
  if (fullGen.environment.packageManager) {
4603
- log(chalk9.gray(` Package manager: ${fullGen.environment.packageManager}`));
4953
+ log(chalk10.gray(` Package manager: ${fullGen.environment.packageManager}`));
4604
4954
  }
4605
4955
  if (fullGen.environment.isMonorepo) {
4606
- log(chalk9.gray(` Monorepo: yes`));
4956
+ log(chalk10.gray(` Monorepo: yes`));
4607
4957
  }
4608
4958
  if (fullGen.environment.targetPackage) {
4609
- log(chalk9.gray(` Target package: ${fullGen.environment.targetPackage}`));
4959
+ log(chalk10.gray(` Target package: ${fullGen.environment.targetPackage}`));
4610
4960
  }
4611
4961
  if (fullGen.issues.length > 0) {
4612
4962
  log("");
4613
- log(chalk9.bold("Issues"));
4963
+ log(chalk10.bold("Issues"));
4614
4964
  for (const issue of fullGen.issues) {
4615
- const prefix2 = issue.severity === "error" ? chalk9.red(">") : issue.severity === "warning" ? chalk9.yellow(">") : chalk9.cyan(">");
4965
+ const prefix2 = issue.severity === "error" ? chalk10.red(">") : issue.severity === "warning" ? chalk10.yellow(">") : chalk10.cyan(">");
4616
4966
  log(`${prefix2} [${issue.code}] ${issue.message}`);
4617
4967
  if (issue.suggestion) {
4618
- log(chalk9.gray(` ${issue.suggestion}`));
4968
+ log(chalk10.gray(` ${issue.suggestion}`));
4619
4969
  }
4620
4970
  }
4621
4971
  }
4622
4972
  }
4623
4973
  if (options.showDiagnostics && result.diagnostics.length > 0) {
4624
4974
  log("");
4625
- log(chalk9.bold("Diagnostics"));
4975
+ log(chalk10.bold("Diagnostics"));
4626
4976
  for (const diagnostic of result.diagnostics) {
4627
- const prefix2 = diagnostic.severity === "error" ? chalk9.red(">") : diagnostic.severity === "warning" ? chalk9.yellow(">") : chalk9.cyan(">");
4977
+ const prefix2 = diagnostic.severity === "error" ? chalk10.red(">") : diagnostic.severity === "warning" ? chalk10.yellow(">") : chalk10.cyan(">");
4628
4978
  log(formatDiagnosticOutput(prefix2, diagnostic, targetDir));
4629
4979
  }
4630
4980
  }
4631
4981
  } catch (commandError) {
4632
- error(chalk9.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
4982
+ error(chalk10.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
4633
4983
  process.exit(1);
4634
4984
  }
4635
4985
  });
@@ -4637,7 +4987,7 @@ function registerSpecCommand(program, dependencies = {}) {
4637
4987
 
4638
4988
  // src/commands/trends.ts
4639
4989
  import * as fs8 from "node:fs";
4640
- import * as path11 from "node:path";
4990
+ import * as path12 from "node:path";
4641
4991
  import {
4642
4992
  formatDelta as formatDelta2,
4643
4993
  getExtendedTrend,
@@ -4647,7 +4997,7 @@ import {
4647
4997
  renderSparkline,
4648
4998
  saveSnapshot
4649
4999
  } from "@doccov/sdk";
4650
- import chalk10 from "chalk";
5000
+ import chalk11 from "chalk";
4651
5001
  function formatDate(timestamp) {
4652
5002
  const date = new Date(timestamp);
4653
5003
  return date.toLocaleDateString("en-US", {
@@ -4660,19 +5010,19 @@ function formatDate(timestamp) {
4660
5010
  }
4661
5011
  function getColorForScore(score) {
4662
5012
  if (score >= 90)
4663
- return chalk10.green;
5013
+ return chalk11.green;
4664
5014
  if (score >= 70)
4665
- return chalk10.yellow;
5015
+ return chalk11.yellow;
4666
5016
  if (score >= 50)
4667
- return chalk10.hex("#FFA500");
4668
- return chalk10.red;
5017
+ return chalk11.hex("#FFA500");
5018
+ return chalk11.red;
4669
5019
  }
4670
5020
  function formatSnapshot(snapshot) {
4671
5021
  const color = getColorForScore(snapshot.coverageScore);
4672
5022
  const date = formatDate(snapshot.timestamp);
4673
5023
  const version = snapshot.version ? ` v${snapshot.version}` : "";
4674
- const commit = snapshot.commit ? chalk10.gray(` (${snapshot.commit.slice(0, 7)})`) : "";
4675
- return `${chalk10.gray(date)} ${color(`${snapshot.coverageScore}%`)} ${snapshot.documentedExports}/${snapshot.totalExports} exports${version}${commit}`;
5024
+ const commit = snapshot.commit ? chalk11.gray(` (${snapshot.commit.slice(0, 7)})`) : "";
5025
+ return `${chalk11.gray(date)} ${color(`${snapshot.coverageScore}%`)} ${snapshot.documentedExports}/${snapshot.totalExports} exports${version}${commit}`;
4676
5026
  }
4677
5027
  function formatWeekDate(timestamp) {
4678
5028
  const date = new Date(timestamp);
@@ -4680,26 +5030,26 @@ function formatWeekDate(timestamp) {
4680
5030
  }
4681
5031
  function formatVelocity(velocity) {
4682
5032
  if (velocity > 0)
4683
- return chalk10.green(`+${velocity}%/day`);
5033
+ return chalk11.green(`+${velocity}%/day`);
4684
5034
  if (velocity < 0)
4685
- return chalk10.red(`${velocity}%/day`);
4686
- return chalk10.gray("0%/day");
5035
+ return chalk11.red(`${velocity}%/day`);
5036
+ return chalk11.gray("0%/day");
4687
5037
  }
4688
5038
  function registerTrendsCommand(program) {
4689
5039
  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("--weekly", "Show weekly summary breakdown").action(async (options) => {
4690
- const cwd = path11.resolve(options.cwd);
5040
+ const cwd = path12.resolve(options.cwd);
4691
5041
  if (options.prune) {
4692
5042
  const keepCount = parseInt(options.prune, 10);
4693
5043
  if (!Number.isNaN(keepCount)) {
4694
5044
  const deleted = pruneHistory(cwd, keepCount);
4695
- console.log(chalk10.green(`Pruned ${deleted} old snapshots, kept ${keepCount} most recent`));
5045
+ console.log(chalk11.green(`Pruned ${deleted} old snapshots, kept ${keepCount} most recent`));
4696
5046
  }
4697
5047
  return;
4698
5048
  }
4699
5049
  if (options.record) {
4700
- const specPath = path11.resolve(cwd, "openpkg.json");
5050
+ const specPath = path12.resolve(cwd, "openpkg.json");
4701
5051
  if (!fs8.existsSync(specPath)) {
4702
- console.error(chalk10.red("No openpkg.json found. Run `doccov spec` first to generate a spec."));
5052
+ console.error(chalk11.red("No openpkg.json found. Run `doccov spec` first to generate a spec."));
4703
5053
  process.exit(1);
4704
5054
  }
4705
5055
  const spin = spinner("Recording coverage snapshot");
@@ -4712,21 +5062,21 @@ function registerTrendsCommand(program) {
4712
5062
  console.log(formatSnapshot(trend.current));
4713
5063
  if (trend.delta !== undefined) {
4714
5064
  const deltaStr = formatDelta2(trend.delta);
4715
- const deltaColor = trend.delta > 0 ? chalk10.green : trend.delta < 0 ? chalk10.red : chalk10.gray;
4716
- console.log(chalk10.gray("Change from previous:"), deltaColor(deltaStr));
5065
+ const deltaColor = trend.delta > 0 ? chalk11.green : trend.delta < 0 ? chalk11.red : chalk11.gray;
5066
+ console.log(chalk11.gray("Change from previous:"), deltaColor(deltaStr));
4717
5067
  }
4718
5068
  return;
4719
5069
  } catch (error) {
4720
5070
  spin.fail("Failed to record snapshot");
4721
- console.error(chalk10.red("Failed to read openpkg.json:"), error instanceof Error ? error.message : error);
5071
+ console.error(chalk11.red("Failed to read openpkg.json:"), error instanceof Error ? error.message : error);
4722
5072
  process.exit(1);
4723
5073
  }
4724
5074
  }
4725
5075
  const snapshots = loadSnapshots(cwd);
4726
5076
  const limit = parseInt(options.limit ?? "10", 10);
4727
5077
  if (snapshots.length === 0) {
4728
- console.log(chalk10.yellow("No coverage history found."));
4729
- console.log(chalk10.gray("Run `doccov trends --record` to save the current coverage."));
5078
+ console.log(chalk11.yellow("No coverage history found."));
5079
+ console.log(chalk11.gray("Run `doccov trends --record` to save the current coverage."));
4730
5080
  return;
4731
5081
  }
4732
5082
  if (options.json) {
@@ -4741,83 +5091,83 @@ function registerTrendsCommand(program) {
4741
5091
  }
4742
5092
  const sparklineData = snapshots.slice(0, 10).map((s) => s.coverageScore).reverse();
4743
5093
  const sparkline = renderSparkline(sparklineData);
4744
- console.log(chalk10.bold("Coverage Trends"));
4745
- console.log(chalk10.gray(`Package: ${snapshots[0].package}`));
4746
- console.log(chalk10.gray(`Sparkline: ${sparkline}`));
5094
+ console.log(chalk11.bold("Coverage Trends"));
5095
+ console.log(chalk11.gray(`Package: ${snapshots[0].package}`));
5096
+ console.log(chalk11.gray(`Sparkline: ${sparkline}`));
4747
5097
  console.log("");
4748
5098
  if (snapshots.length >= 2) {
4749
5099
  const oldest = snapshots[snapshots.length - 1];
4750
5100
  const newest = snapshots[0];
4751
5101
  const overallDelta = newest.coverageScore - oldest.coverageScore;
4752
5102
  const deltaStr = formatDelta2(overallDelta);
4753
- const deltaColor = overallDelta > 0 ? chalk10.green : overallDelta < 0 ? chalk10.red : chalk10.gray;
4754
- console.log(chalk10.gray("Overall trend:"), deltaColor(deltaStr), chalk10.gray(`(${snapshots.length} snapshots)`));
5103
+ const deltaColor = overallDelta > 0 ? chalk11.green : overallDelta < 0 ? chalk11.red : chalk11.gray;
5104
+ console.log(chalk11.gray("Overall trend:"), deltaColor(deltaStr), chalk11.gray(`(${snapshots.length} snapshots)`));
4755
5105
  console.log("");
4756
5106
  }
4757
5107
  if (options.extended) {
4758
- const specPath = path11.resolve(cwd, "openpkg.json");
5108
+ const specPath = path12.resolve(cwd, "openpkg.json");
4759
5109
  if (fs8.existsSync(specPath)) {
4760
5110
  try {
4761
5111
  const specContent = fs8.readFileSync(specPath, "utf-8");
4762
5112
  const spec = JSON.parse(specContent);
4763
5113
  const extended = getExtendedTrend(spec, cwd);
4764
- console.log(chalk10.bold("Extended Analysis"));
4765
- console.log(chalk10.gray("90-day retention"));
5114
+ console.log(chalk11.bold("Extended Analysis"));
5115
+ console.log(chalk11.gray("90-day retention"));
4766
5116
  console.log("");
4767
5117
  console.log(" Velocity:");
4768
5118
  console.log(` 7-day: ${formatVelocity(extended.velocity7d)}`);
4769
5119
  console.log(` 30-day: ${formatVelocity(extended.velocity30d)}`);
4770
5120
  console.log(` 90-day: ${formatVelocity(extended.velocity90d)}`);
4771
5121
  console.log("");
4772
- const projColor = extended.projected30d >= extended.trend.current.coverageScore ? chalk10.green : chalk10.red;
5122
+ const projColor = extended.projected30d >= extended.trend.current.coverageScore ? chalk11.green : chalk11.red;
4773
5123
  console.log(` Projected (30d): ${projColor(`${extended.projected30d}%`)}`);
4774
- console.log(` All-time high: ${chalk10.green(`${extended.allTimeHigh}%`)}`);
4775
- console.log(` All-time low: ${chalk10.red(`${extended.allTimeLow}%`)}`);
5124
+ console.log(` All-time high: ${chalk11.green(`${extended.allTimeHigh}%`)}`);
5125
+ console.log(` All-time low: ${chalk11.red(`${extended.allTimeLow}%`)}`);
4776
5126
  if (extended.dataRange) {
4777
5127
  const startDate = formatWeekDate(extended.dataRange.start);
4778
5128
  const endDate = formatWeekDate(extended.dataRange.end);
4779
- console.log(chalk10.gray(` Data range: ${startDate} - ${endDate}`));
5129
+ console.log(chalk11.gray(` Data range: ${startDate} - ${endDate}`));
4780
5130
  }
4781
5131
  console.log("");
4782
5132
  if (options.weekly && extended.weeklySummaries.length > 0) {
4783
- console.log(chalk10.bold("Weekly Summary"));
5133
+ console.log(chalk11.bold("Weekly Summary"));
4784
5134
  const weekLimit = Math.min(extended.weeklySummaries.length, 8);
4785
5135
  for (let i = 0;i < weekLimit; i++) {
4786
5136
  const week = extended.weeklySummaries[i];
4787
5137
  const weekStart = formatWeekDate(week.weekStart);
4788
5138
  const weekEnd = formatWeekDate(week.weekEnd);
4789
- const deltaColor = week.delta > 0 ? chalk10.green : week.delta < 0 ? chalk10.red : chalk10.gray;
5139
+ const deltaColor = week.delta > 0 ? chalk11.green : week.delta < 0 ? chalk11.red : chalk11.gray;
4790
5140
  const deltaStr = week.delta > 0 ? `+${week.delta}%` : `${week.delta}%`;
4791
5141
  console.log(` ${weekStart} - ${weekEnd}: ${week.avgCoverage}% avg ${deltaColor(deltaStr)} (${week.snapshotCount} snapshots)`);
4792
5142
  }
4793
5143
  if (extended.weeklySummaries.length > weekLimit) {
4794
- console.log(chalk10.gray(` ... and ${extended.weeklySummaries.length - weekLimit} more weeks`));
5144
+ console.log(chalk11.gray(` ... and ${extended.weeklySummaries.length - weekLimit} more weeks`));
4795
5145
  }
4796
5146
  console.log("");
4797
5147
  }
4798
5148
  } catch {
4799
- console.log(chalk10.yellow("Could not load openpkg.json for extended analysis"));
5149
+ console.log(chalk11.yellow("Could not load openpkg.json for extended analysis"));
4800
5150
  console.log("");
4801
5151
  }
4802
5152
  }
4803
5153
  }
4804
- console.log(chalk10.bold("History"));
5154
+ console.log(chalk11.bold("History"));
4805
5155
  const displaySnapshots = snapshots.slice(0, limit);
4806
5156
  for (let i = 0;i < displaySnapshots.length; i++) {
4807
5157
  const snapshot = displaySnapshots[i];
4808
- const prefix2 = i === 0 ? chalk10.cyan("→") : " ";
5158
+ const prefix2 = i === 0 ? chalk11.cyan("→") : " ";
4809
5159
  console.log(`${prefix2} ${formatSnapshot(snapshot)}`);
4810
5160
  }
4811
5161
  if (snapshots.length > limit) {
4812
- console.log(chalk10.gray(` ... and ${snapshots.length - limit} more`));
5162
+ console.log(chalk11.gray(` ... and ${snapshots.length - limit} more`));
4813
5163
  }
4814
5164
  });
4815
5165
  }
4816
5166
 
4817
5167
  // src/cli.ts
4818
5168
  var __filename2 = fileURLToPath(import.meta.url);
4819
- var __dirname2 = path12.dirname(__filename2);
4820
- var packageJson = JSON.parse(readFileSync7(path12.join(__dirname2, "../package.json"), "utf-8"));
5169
+ var __dirname2 = path13.dirname(__filename2);
5170
+ var packageJson = JSON.parse(readFileSync7(path13.join(__dirname2, "../package.json"), "utf-8"));
4821
5171
  var program = new Command;
4822
5172
  program.name("doccov").description("DocCov - Documentation coverage and drift detection for TypeScript").version(packageJson.version);
4823
5173
  registerCheckCommand(program);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doccov/cli",
3
- "version": "0.30.7",
3
+ "version": "0.31.0",
4
4
  "description": "DocCov CLI - Documentation coverage and drift detection for TypeScript",
5
5
  "keywords": [
6
6
  "typescript",
@@ -48,8 +48,8 @@
48
48
  "dependencies": {
49
49
  "@ai-sdk/anthropic": "^1.0.0",
50
50
  "@ai-sdk/openai": "^1.0.0",
51
- "@doccov/sdk": "^0.30.7",
52
- "@doccov/spec": "^0.27.0",
51
+ "@doccov/sdk": "^0.31.0",
52
+ "@doccov/spec": "^0.31.0",
53
53
  "@inquirer/prompts": "^7.8.0",
54
54
  "@openpkg-ts/spec": "^0.23.0",
55
55
  "ai": "^4.0.0",