@doccov/cli 0.13.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +203 -162
- package/package.json +2 -3
package/dist/cli.js
CHANGED
|
@@ -188,7 +188,7 @@ import {
|
|
|
188
188
|
import {
|
|
189
189
|
DRIFT_CATEGORIES as DRIFT_CATEGORIES2
|
|
190
190
|
} from "@openpkg-ts/spec";
|
|
191
|
-
import
|
|
191
|
+
import chalk3 from "chalk";
|
|
192
192
|
|
|
193
193
|
// src/reports/diff-markdown.ts
|
|
194
194
|
import * as path2 from "node:path";
|
|
@@ -770,6 +770,57 @@ async function parseAssertionsWithLLM(code) {
|
|
|
770
770
|
}
|
|
771
771
|
}
|
|
772
772
|
|
|
773
|
+
// src/utils/progress.ts
|
|
774
|
+
import chalk2 from "chalk";
|
|
775
|
+
class StepProgress {
|
|
776
|
+
steps;
|
|
777
|
+
currentStep = 0;
|
|
778
|
+
startTime;
|
|
779
|
+
stepStartTime;
|
|
780
|
+
constructor(steps) {
|
|
781
|
+
this.steps = steps;
|
|
782
|
+
this.startTime = Date.now();
|
|
783
|
+
this.stepStartTime = Date.now();
|
|
784
|
+
}
|
|
785
|
+
start(stepIndex) {
|
|
786
|
+
this.currentStep = stepIndex ?? 0;
|
|
787
|
+
this.stepStartTime = Date.now();
|
|
788
|
+
this.render();
|
|
789
|
+
}
|
|
790
|
+
next() {
|
|
791
|
+
this.completeCurrentStep();
|
|
792
|
+
this.currentStep++;
|
|
793
|
+
if (this.currentStep < this.steps.length) {
|
|
794
|
+
this.stepStartTime = Date.now();
|
|
795
|
+
this.render();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
complete(message) {
|
|
799
|
+
this.completeCurrentStep();
|
|
800
|
+
if (message) {
|
|
801
|
+
const elapsed = ((Date.now() - this.startTime) / 1000).toFixed(1);
|
|
802
|
+
console.log(`${chalk2.green("✓")} ${message} ${chalk2.dim(`(${elapsed}s)`)}`);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
render() {
|
|
806
|
+
const step = this.steps[this.currentStep];
|
|
807
|
+
if (!step)
|
|
808
|
+
return;
|
|
809
|
+
const label = step.activeLabel ?? step.label;
|
|
810
|
+
const prefix = chalk2.dim(`[${this.currentStep + 1}/${this.steps.length}]`);
|
|
811
|
+
process.stdout.write(`\r${prefix} ${chalk2.cyan(label)}...`);
|
|
812
|
+
}
|
|
813
|
+
completeCurrentStep() {
|
|
814
|
+
const step = this.steps[this.currentStep];
|
|
815
|
+
if (!step)
|
|
816
|
+
return;
|
|
817
|
+
const elapsed = ((Date.now() - this.stepStartTime) / 1000).toFixed(1);
|
|
818
|
+
const prefix = chalk2.dim(`[${this.currentStep + 1}/${this.steps.length}]`);
|
|
819
|
+
process.stdout.write(`\r${" ".repeat(80)}\r`);
|
|
820
|
+
console.log(`${prefix} ${step.label} ${chalk2.green("✓")} ${chalk2.dim(`(${elapsed}s)`)}`);
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
773
824
|
// src/utils/validation.ts
|
|
774
825
|
function clampPercentage(value, fallback = 80) {
|
|
775
826
|
if (Number.isNaN(value))
|
|
@@ -812,24 +863,32 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
812
863
|
};
|
|
813
864
|
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-coverage <percentage>", "Minimum docs coverage percentage (0-100)", (value) => Number(value)).option("--max-drift <percentage>", "Maximum drift percentage allowed (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("--fix", "Auto-fix drift issues").option("--write", "Alias for --fix").option("--dry-run", "Preview fixes without writing (requires --fix)").option("--format <format>", "Output format: text, json, markdown, html, github", "text").option("-o, --output <file>", "Custom output path (overrides default .doccov/ path)").option("--stdout", "Output to stdout instead of writing to .doccov/").option("--update-snapshot", "Force regenerate .doccov/report.json").option("--limit <n>", "Max exports to show in report tables", "20").option("--max-type-depth <number>", "Maximum depth for type conversion (default: 20)").option("--no-cache", "Bypass spec cache and force regeneration").action(async (entry, options) => {
|
|
814
865
|
try {
|
|
866
|
+
const validations = parseExamplesFlag(options.examples);
|
|
867
|
+
const hasExamples = validations.length > 0;
|
|
868
|
+
const stepList = [
|
|
869
|
+
{ label: "Resolved target", activeLabel: "Resolving target" },
|
|
870
|
+
{ label: "Loaded config", activeLabel: "Loading config" },
|
|
871
|
+
{ label: "Generated spec", activeLabel: "Generating spec" },
|
|
872
|
+
{ label: "Enriched spec", activeLabel: "Enriching spec" },
|
|
873
|
+
...hasExamples ? [{ label: "Validated examples", activeLabel: "Validating examples" }] : [],
|
|
874
|
+
{ label: "Processed results", activeLabel: "Processing results" }
|
|
875
|
+
];
|
|
876
|
+
const steps = new StepProgress(stepList);
|
|
877
|
+
steps.start();
|
|
815
878
|
const fileSystem = new NodeFileSystem(options.cwd);
|
|
816
879
|
const resolved = await resolveTarget(fileSystem, {
|
|
817
880
|
cwd: options.cwd,
|
|
818
881
|
package: options.package,
|
|
819
882
|
entry
|
|
820
883
|
});
|
|
821
|
-
const { targetDir, entryFile
|
|
822
|
-
|
|
823
|
-
log(chalk2.gray(`Found package at ${packageInfo.path}`));
|
|
824
|
-
}
|
|
825
|
-
if (!entry) {
|
|
826
|
-
log(chalk2.gray(`Auto-detected entry point: ${entryPointInfo.path} (from ${entryPointInfo.source})`));
|
|
827
|
-
}
|
|
884
|
+
const { targetDir, entryFile } = resolved;
|
|
885
|
+
steps.next();
|
|
828
886
|
const config = await loadDocCovConfig(targetDir);
|
|
829
887
|
const minCoverageRaw = options.minCoverage ?? config?.check?.minCoverage;
|
|
830
888
|
const minCoverage = minCoverageRaw !== undefined ? clampPercentage(minCoverageRaw) : undefined;
|
|
831
889
|
const maxDriftRaw = options.maxDrift ?? config?.check?.maxDrift;
|
|
832
890
|
const maxDrift = maxDriftRaw !== undefined ? clampPercentage(maxDriftRaw) : undefined;
|
|
891
|
+
steps.next();
|
|
833
892
|
const resolveExternalTypes = !options.skipResolve;
|
|
834
893
|
let specResult;
|
|
835
894
|
const doccov = createDocCov({
|
|
@@ -839,14 +898,13 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
839
898
|
cwd: options.cwd
|
|
840
899
|
});
|
|
841
900
|
specResult = await doccov.analyzeFileWithDiagnostics(entryFile);
|
|
842
|
-
if (specResult.fromCache) {
|
|
843
|
-
log(chalk2.gray("Using cached spec"));
|
|
844
|
-
}
|
|
845
901
|
if (!specResult) {
|
|
846
902
|
throw new Error("Failed to analyze documentation coverage.");
|
|
847
903
|
}
|
|
904
|
+
steps.next();
|
|
848
905
|
const spec = enrichSpec(specResult.spec);
|
|
849
906
|
const format = options.format ?? "text";
|
|
907
|
+
steps.next();
|
|
850
908
|
const specWarnings = specResult.diagnostics.filter((d) => d.severity === "warning");
|
|
851
909
|
const specInfos = specResult.diagnostics.filter((d) => d.severity === "info");
|
|
852
910
|
const shouldFix = options.fix || options.write;
|
|
@@ -856,11 +914,10 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
856
914
|
violations.push({ exportName: exp.name, violation: v });
|
|
857
915
|
}
|
|
858
916
|
}
|
|
859
|
-
const validations = parseExamplesFlag(options.examples);
|
|
860
917
|
let exampleResult;
|
|
861
918
|
const typecheckErrors = [];
|
|
862
919
|
const runtimeDrifts = [];
|
|
863
|
-
if (
|
|
920
|
+
if (hasExamples) {
|
|
864
921
|
exampleResult = await validateExamples(spec.exports ?? [], {
|
|
865
922
|
validations,
|
|
866
923
|
packagePath: targetDir,
|
|
@@ -891,22 +948,23 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
891
948
|
});
|
|
892
949
|
}
|
|
893
950
|
}
|
|
951
|
+
steps.next();
|
|
894
952
|
}
|
|
895
953
|
const coverageScore = spec.docs?.coverageScore ?? 0;
|
|
896
954
|
const allDriftExports = [...collectDrift(spec.exports ?? []), ...runtimeDrifts];
|
|
897
|
-
let driftExports =
|
|
955
|
+
let driftExports = hasExamples ? allDriftExports : allDriftExports.filter((d) => d.category !== "example");
|
|
898
956
|
const fixedDriftKeys = new Set;
|
|
899
957
|
if (shouldFix && driftExports.length > 0) {
|
|
900
958
|
const allDrifts = collectDriftsFromExports(spec.exports ?? []);
|
|
901
959
|
if (allDrifts.length > 0) {
|
|
902
960
|
const { fixable, nonFixable } = categorizeDrifts(allDrifts.map((d) => d.drift));
|
|
903
961
|
if (fixable.length === 0) {
|
|
904
|
-
log(
|
|
962
|
+
log(chalk3.yellow(`Found ${nonFixable.length} drift issue(s), but none are auto-fixable.`));
|
|
905
963
|
} else {
|
|
906
964
|
log("");
|
|
907
|
-
log(
|
|
965
|
+
log(chalk3.bold(`Found ${fixable.length} fixable issue(s)`));
|
|
908
966
|
if (nonFixable.length > 0) {
|
|
909
|
-
log(
|
|
967
|
+
log(chalk3.gray(`(${nonFixable.length} non-fixable issue(s) skipped)`));
|
|
910
968
|
}
|
|
911
969
|
log("");
|
|
912
970
|
const groupedDrifts = groupByExport(allDrifts.filter((d) => fixable.includes(d.drift)));
|
|
@@ -914,22 +972,22 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
914
972
|
const editsByFile = new Map;
|
|
915
973
|
for (const [exp, drifts] of groupedDrifts) {
|
|
916
974
|
if (!exp.source?.file) {
|
|
917
|
-
log(
|
|
975
|
+
log(chalk3.gray(` Skipping ${exp.name}: no source location`));
|
|
918
976
|
continue;
|
|
919
977
|
}
|
|
920
978
|
if (exp.source.file.endsWith(".d.ts")) {
|
|
921
|
-
log(
|
|
979
|
+
log(chalk3.gray(` Skipping ${exp.name}: declaration file`));
|
|
922
980
|
continue;
|
|
923
981
|
}
|
|
924
982
|
const filePath = path4.resolve(targetDir, exp.source.file);
|
|
925
983
|
if (!fs2.existsSync(filePath)) {
|
|
926
|
-
log(
|
|
984
|
+
log(chalk3.gray(` Skipping ${exp.name}: file not found`));
|
|
927
985
|
continue;
|
|
928
986
|
}
|
|
929
987
|
const sourceFile = createSourceFile(filePath);
|
|
930
988
|
const location = findJSDocLocation(sourceFile, exp.name, exp.source.line);
|
|
931
989
|
if (!location) {
|
|
932
|
-
log(
|
|
990
|
+
log(chalk3.gray(` Skipping ${exp.name}: could not find declaration`));
|
|
933
991
|
continue;
|
|
934
992
|
}
|
|
935
993
|
let existingPatch = {};
|
|
@@ -962,26 +1020,26 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
962
1020
|
}
|
|
963
1021
|
if (edits.length > 0) {
|
|
964
1022
|
if (options.dryRun) {
|
|
965
|
-
log(
|
|
1023
|
+
log(chalk3.bold("Dry run - changes that would be made:"));
|
|
966
1024
|
log("");
|
|
967
1025
|
for (const [filePath, fileEdits] of editsByFile) {
|
|
968
1026
|
const relativePath = path4.relative(targetDir, filePath);
|
|
969
|
-
log(
|
|
1027
|
+
log(chalk3.cyan(` ${relativePath}:`));
|
|
970
1028
|
for (const { export: exp, edit, fixes } of fileEdits) {
|
|
971
1029
|
const lineInfo = edit.hasExisting ? `lines ${edit.startLine + 1}-${edit.endLine + 1}` : `line ${edit.startLine + 1}`;
|
|
972
|
-
log(` ${
|
|
1030
|
+
log(` ${chalk3.bold(exp.name)} [${lineInfo}]`);
|
|
973
1031
|
for (const fix of fixes) {
|
|
974
|
-
log(
|
|
1032
|
+
log(chalk3.green(` + ${fix.description}`));
|
|
975
1033
|
}
|
|
976
1034
|
}
|
|
977
1035
|
log("");
|
|
978
1036
|
}
|
|
979
|
-
log(
|
|
1037
|
+
log(chalk3.gray("Run without --dry-run to apply these changes."));
|
|
980
1038
|
} else {
|
|
981
1039
|
const applyResult = await applyEdits(edits);
|
|
982
1040
|
if (applyResult.errors.length > 0) {
|
|
983
1041
|
for (const err of applyResult.errors) {
|
|
984
|
-
error(
|
|
1042
|
+
error(chalk3.red(` ${err.file}: ${err.error}`));
|
|
985
1043
|
}
|
|
986
1044
|
}
|
|
987
1045
|
}
|
|
@@ -992,6 +1050,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
992
1050
|
driftExports = driftExports.filter((d) => !fixedDriftKeys.has(`${d.name}:${d.issue}`));
|
|
993
1051
|
}
|
|
994
1052
|
}
|
|
1053
|
+
steps.complete("Check complete");
|
|
995
1054
|
if (format !== "text") {
|
|
996
1055
|
const limit = parseInt(options.limit, 10) || 20;
|
|
997
1056
|
const stats = computeStats(spec);
|
|
@@ -1051,15 +1110,15 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
1051
1110
|
if (specWarnings.length > 0 || specInfos.length > 0) {
|
|
1052
1111
|
log("");
|
|
1053
1112
|
for (const diag of specWarnings) {
|
|
1054
|
-
log(
|
|
1113
|
+
log(chalk3.yellow(`⚠ ${diag.message}`));
|
|
1055
1114
|
if (diag.suggestion) {
|
|
1056
|
-
log(
|
|
1115
|
+
log(chalk3.gray(` ${diag.suggestion}`));
|
|
1057
1116
|
}
|
|
1058
1117
|
}
|
|
1059
1118
|
for (const diag of specInfos) {
|
|
1060
|
-
log(
|
|
1119
|
+
log(chalk3.cyan(`ℹ ${diag.message}`));
|
|
1061
1120
|
if (diag.suggestion) {
|
|
1062
|
-
log(
|
|
1121
|
+
log(chalk3.gray(` ${diag.suggestion}`));
|
|
1063
1122
|
}
|
|
1064
1123
|
}
|
|
1065
1124
|
}
|
|
@@ -1069,23 +1128,23 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
1069
1128
|
const errorCount = violations.filter((v) => v.violation.severity === "error").length;
|
|
1070
1129
|
const warnCount = violations.filter((v) => v.violation.severity === "warn").length;
|
|
1071
1130
|
log("");
|
|
1072
|
-
log(
|
|
1131
|
+
log(chalk3.bold(`${pkgName}${pkgVersion ? `@${pkgVersion}` : ""}`));
|
|
1073
1132
|
log("");
|
|
1074
1133
|
log(` Exports: ${totalExports}`);
|
|
1075
1134
|
if (minCoverage !== undefined) {
|
|
1076
1135
|
if (coverageFailed) {
|
|
1077
|
-
log(
|
|
1136
|
+
log(chalk3.red(` Coverage: ✗ ${coverageScore}%`) + chalk3.dim(` (min ${minCoverage}%)`));
|
|
1078
1137
|
} else {
|
|
1079
|
-
log(
|
|
1138
|
+
log(chalk3.green(` Coverage: ✓ ${coverageScore}%`) + chalk3.dim(` (min ${minCoverage}%)`));
|
|
1080
1139
|
}
|
|
1081
1140
|
} else {
|
|
1082
1141
|
log(` Coverage: ${coverageScore}%`);
|
|
1083
1142
|
}
|
|
1084
1143
|
if (maxDrift !== undefined) {
|
|
1085
1144
|
if (driftFailed) {
|
|
1086
|
-
log(
|
|
1145
|
+
log(chalk3.red(` Drift: ✗ ${driftScore}%`) + chalk3.dim(` (max ${maxDrift}%)`));
|
|
1087
1146
|
} else {
|
|
1088
|
-
log(
|
|
1147
|
+
log(chalk3.green(` Drift: ✓ ${driftScore}%`) + chalk3.dim(` (max ${maxDrift}%)`));
|
|
1089
1148
|
}
|
|
1090
1149
|
} else {
|
|
1091
1150
|
log(` Drift: ${driftScore}%`);
|
|
@@ -1095,7 +1154,7 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
1095
1154
|
if (typecheckCount > 0) {
|
|
1096
1155
|
log(` Examples: ${typecheckCount} type errors`);
|
|
1097
1156
|
} else {
|
|
1098
|
-
log(
|
|
1157
|
+
log(chalk3.green(` Examples: ✓ validated`));
|
|
1099
1158
|
}
|
|
1100
1159
|
}
|
|
1101
1160
|
if (errorCount > 0 || warnCount > 0) {
|
|
@@ -1117,24 +1176,24 @@ function registerCheckCommand(program, dependencies = {}) {
|
|
|
1117
1176
|
thresholdParts.push(`drift ${driftScore}% ≤ ${maxDrift}%`);
|
|
1118
1177
|
}
|
|
1119
1178
|
if (thresholdParts.length > 0) {
|
|
1120
|
-
log(
|
|
1179
|
+
log(chalk3.green(`✓ Check passed (${thresholdParts.join(", ")})`));
|
|
1121
1180
|
} else {
|
|
1122
|
-
log(
|
|
1123
|
-
log(
|
|
1181
|
+
log(chalk3.green("✓ Check passed"));
|
|
1182
|
+
log(chalk3.dim(" No thresholds configured. Use --min-coverage or --max-drift to enforce."));
|
|
1124
1183
|
}
|
|
1125
1184
|
return;
|
|
1126
1185
|
}
|
|
1127
1186
|
if (hasQualityErrors) {
|
|
1128
|
-
log(
|
|
1187
|
+
log(chalk3.red(`✗ ${errorCount} quality errors`));
|
|
1129
1188
|
}
|
|
1130
1189
|
if (hasTypecheckErrors) {
|
|
1131
|
-
log(
|
|
1190
|
+
log(chalk3.red(`✗ ${typecheckErrors.length} example type errors`));
|
|
1132
1191
|
}
|
|
1133
1192
|
log("");
|
|
1134
|
-
log(
|
|
1193
|
+
log(chalk3.dim("Use --format json or --format markdown for detailed reports"));
|
|
1135
1194
|
process.exit(1);
|
|
1136
1195
|
} catch (commandError) {
|
|
1137
|
-
error(
|
|
1196
|
+
error(chalk3.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
1138
1197
|
process.exit(1);
|
|
1139
1198
|
}
|
|
1140
1199
|
});
|
|
@@ -1171,7 +1230,7 @@ import {
|
|
|
1171
1230
|
hashString,
|
|
1172
1231
|
parseMarkdownFiles
|
|
1173
1232
|
} from "@doccov/sdk";
|
|
1174
|
-
import
|
|
1233
|
+
import chalk4 from "chalk";
|
|
1175
1234
|
import { glob } from "glob";
|
|
1176
1235
|
|
|
1177
1236
|
// src/utils/docs-impact-ai.ts
|
|
@@ -1317,8 +1376,8 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
1317
1376
|
silent: true
|
|
1318
1377
|
});
|
|
1319
1378
|
}
|
|
1320
|
-
const cacheNote = fromCache ?
|
|
1321
|
-
log(
|
|
1379
|
+
const cacheNote = fromCache ? chalk4.cyan(" (cached)") : "";
|
|
1380
|
+
log(chalk4.dim(`Report: ${jsonPath}`) + cacheNote);
|
|
1322
1381
|
}
|
|
1323
1382
|
break;
|
|
1324
1383
|
case "json": {
|
|
@@ -1376,18 +1435,18 @@ function registerDiffCommand(program, dependencies = {}) {
|
|
|
1376
1435
|
checks
|
|
1377
1436
|
});
|
|
1378
1437
|
if (failures.length > 0) {
|
|
1379
|
-
log(
|
|
1438
|
+
log(chalk4.red(`
|
|
1380
1439
|
✗ Check failed`));
|
|
1381
1440
|
for (const f of failures) {
|
|
1382
|
-
log(
|
|
1441
|
+
log(chalk4.red(` - ${f}`));
|
|
1383
1442
|
}
|
|
1384
1443
|
process.exitCode = 1;
|
|
1385
1444
|
} else if (options.strict || minCoverage !== undefined || maxDrift !== undefined) {
|
|
1386
|
-
log(
|
|
1445
|
+
log(chalk4.green(`
|
|
1387
1446
|
✓ All checks passed`));
|
|
1388
1447
|
}
|
|
1389
1448
|
} catch (commandError) {
|
|
1390
|
-
error(
|
|
1449
|
+
error(chalk4.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
1391
1450
|
process.exitCode = 1;
|
|
1392
1451
|
}
|
|
1393
1452
|
});
|
|
@@ -1414,7 +1473,7 @@ async function generateDiff(baseSpec, headSpec, options, config, log) {
|
|
|
1414
1473
|
if (!docsPatterns || docsPatterns.length === 0) {
|
|
1415
1474
|
if (config?.docs?.include) {
|
|
1416
1475
|
docsPatterns = config.docs.include;
|
|
1417
|
-
log(
|
|
1476
|
+
log(chalk4.gray(`Using docs patterns from config: ${docsPatterns.join(", ")}`));
|
|
1418
1477
|
}
|
|
1419
1478
|
}
|
|
1420
1479
|
if (docsPatterns && docsPatterns.length > 0) {
|
|
@@ -1437,46 +1496,46 @@ function loadSpec(filePath, readFileSync2) {
|
|
|
1437
1496
|
}
|
|
1438
1497
|
function printSummary(diff, baseName, headName, fromCache, log) {
|
|
1439
1498
|
log("");
|
|
1440
|
-
const cacheIndicator = fromCache ?
|
|
1441
|
-
log(
|
|
1499
|
+
const cacheIndicator = fromCache ? chalk4.cyan(" (cached)") : "";
|
|
1500
|
+
log(chalk4.bold(`Comparing: ${baseName} → ${headName}`) + cacheIndicator);
|
|
1442
1501
|
log("─".repeat(40));
|
|
1443
1502
|
log("");
|
|
1444
|
-
const coverageColor = diff.coverageDelta > 0 ?
|
|
1503
|
+
const coverageColor = diff.coverageDelta > 0 ? chalk4.green : diff.coverageDelta < 0 ? chalk4.red : chalk4.gray;
|
|
1445
1504
|
const coverageSign = diff.coverageDelta > 0 ? "+" : "";
|
|
1446
1505
|
log(` Coverage: ${diff.oldCoverage}% → ${diff.newCoverage}% ${coverageColor(`(${coverageSign}${diff.coverageDelta}%)`)}`);
|
|
1447
1506
|
const breakingCount = diff.breaking.length;
|
|
1448
1507
|
const highSeverity = diff.categorizedBreaking?.filter((c) => c.severity === "high").length ?? 0;
|
|
1449
1508
|
if (breakingCount > 0) {
|
|
1450
|
-
const severityNote = highSeverity > 0 ?
|
|
1451
|
-
log(` Breaking: ${
|
|
1509
|
+
const severityNote = highSeverity > 0 ? chalk4.red(` (${highSeverity} high severity)`) : "";
|
|
1510
|
+
log(` Breaking: ${chalk4.red(breakingCount)} changes${severityNote}`);
|
|
1452
1511
|
} else {
|
|
1453
|
-
log(` Breaking: ${
|
|
1512
|
+
log(` Breaking: ${chalk4.green("0")} changes`);
|
|
1454
1513
|
}
|
|
1455
1514
|
const newCount = diff.nonBreaking.length;
|
|
1456
1515
|
const undocCount = diff.newUndocumented.length;
|
|
1457
1516
|
if (newCount > 0) {
|
|
1458
|
-
const undocNote = undocCount > 0 ?
|
|
1459
|
-
log(` New: ${
|
|
1517
|
+
const undocNote = undocCount > 0 ? chalk4.yellow(` (${undocCount} undocumented)`) : "";
|
|
1518
|
+
log(` New: ${chalk4.green(newCount)} exports${undocNote}`);
|
|
1460
1519
|
}
|
|
1461
1520
|
if (diff.driftIntroduced > 0 || diff.driftResolved > 0) {
|
|
1462
1521
|
const parts = [];
|
|
1463
1522
|
if (diff.driftIntroduced > 0)
|
|
1464
|
-
parts.push(
|
|
1523
|
+
parts.push(chalk4.red(`+${diff.driftIntroduced}`));
|
|
1465
1524
|
if (diff.driftResolved > 0)
|
|
1466
|
-
parts.push(
|
|
1525
|
+
parts.push(chalk4.green(`-${diff.driftResolved}`));
|
|
1467
1526
|
log(` Drift: ${parts.join(", ")}`);
|
|
1468
1527
|
}
|
|
1469
1528
|
log("");
|
|
1470
1529
|
}
|
|
1471
1530
|
async function printAISummary(diff, log) {
|
|
1472
1531
|
if (!isAIDocsAnalysisAvailable()) {
|
|
1473
|
-
log(
|
|
1532
|
+
log(chalk4.yellow(`
|
|
1474
1533
|
⚠ AI analysis unavailable (set OPENAI_API_KEY or ANTHROPIC_API_KEY)`));
|
|
1475
1534
|
return;
|
|
1476
1535
|
}
|
|
1477
1536
|
if (!diff.docsImpact)
|
|
1478
1537
|
return;
|
|
1479
|
-
log(
|
|
1538
|
+
log(chalk4.gray(`
|
|
1480
1539
|
Generating AI summary...`));
|
|
1481
1540
|
const impacts = diff.docsImpact.impactedFiles.flatMap((f) => f.references.map((r) => ({
|
|
1482
1541
|
file: f.file,
|
|
@@ -1487,8 +1546,8 @@ Generating AI summary...`));
|
|
|
1487
1546
|
const summary = await generateImpactSummary(impacts);
|
|
1488
1547
|
if (summary) {
|
|
1489
1548
|
log("");
|
|
1490
|
-
log(
|
|
1491
|
-
log(
|
|
1549
|
+
log(chalk4.bold("AI Summary"));
|
|
1550
|
+
log(chalk4.cyan(` ${summary}`));
|
|
1492
1551
|
}
|
|
1493
1552
|
}
|
|
1494
1553
|
function validateDiff(diff, headSpec, options) {
|
|
@@ -1569,7 +1628,7 @@ function printGitHubAnnotations(diff, log) {
|
|
|
1569
1628
|
|
|
1570
1629
|
// src/commands/info.ts
|
|
1571
1630
|
import { DocCov as DocCov2, enrichSpec as enrichSpec2, NodeFileSystem as NodeFileSystem2, resolveTarget as resolveTarget2 } from "@doccov/sdk";
|
|
1572
|
-
import
|
|
1631
|
+
import chalk5 from "chalk";
|
|
1573
1632
|
function registerInfoCommand(program) {
|
|
1574
1633
|
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) => {
|
|
1575
1634
|
try {
|
|
@@ -1591,14 +1650,14 @@ function registerInfoCommand(program) {
|
|
|
1591
1650
|
const spec = enrichSpec2(specResult.spec);
|
|
1592
1651
|
const stats = computeStats(spec);
|
|
1593
1652
|
console.log("");
|
|
1594
|
-
console.log(
|
|
1653
|
+
console.log(chalk5.bold(`${stats.packageName}@${stats.version}`));
|
|
1595
1654
|
console.log("");
|
|
1596
|
-
console.log(` Exports: ${
|
|
1597
|
-
console.log(` Coverage: ${
|
|
1598
|
-
console.log(` Drift: ${
|
|
1655
|
+
console.log(` Exports: ${chalk5.bold(stats.totalExports.toString())}`);
|
|
1656
|
+
console.log(` Coverage: ${chalk5.bold(`${stats.coverageScore}%`)}`);
|
|
1657
|
+
console.log(` Drift: ${chalk5.bold(`${stats.driftScore}%`)}`);
|
|
1599
1658
|
console.log("");
|
|
1600
1659
|
} catch (err) {
|
|
1601
|
-
console.error(
|
|
1660
|
+
console.error(chalk5.red("Error:"), err instanceof Error ? err.message : err);
|
|
1602
1661
|
process.exit(1);
|
|
1603
1662
|
}
|
|
1604
1663
|
});
|
|
@@ -1607,7 +1666,7 @@ function registerInfoCommand(program) {
|
|
|
1607
1666
|
// src/commands/init.ts
|
|
1608
1667
|
import * as fs4 from "node:fs";
|
|
1609
1668
|
import * as path6 from "node:path";
|
|
1610
|
-
import
|
|
1669
|
+
import chalk6 from "chalk";
|
|
1611
1670
|
var defaultDependencies3 = {
|
|
1612
1671
|
fileExists: fs4.existsSync,
|
|
1613
1672
|
writeFileSync: fs4.writeFileSync,
|
|
@@ -1624,31 +1683,31 @@ function registerInitCommand(program, dependencies = {}) {
|
|
|
1624
1683
|
const cwd = path6.resolve(options.cwd);
|
|
1625
1684
|
const formatOption = String(options.format ?? "auto").toLowerCase();
|
|
1626
1685
|
if (!isValidFormat(formatOption)) {
|
|
1627
|
-
error(
|
|
1686
|
+
error(chalk6.red(`Invalid format "${formatOption}". Use auto, mjs, js, or cjs.`));
|
|
1628
1687
|
process.exitCode = 1;
|
|
1629
1688
|
return;
|
|
1630
1689
|
}
|
|
1631
1690
|
const existing = findExistingConfig(cwd, fileExists2);
|
|
1632
1691
|
if (existing) {
|
|
1633
|
-
error(
|
|
1692
|
+
error(chalk6.red(`A DocCov config already exists at ${path6.relative(cwd, existing) || "./doccov.config.*"}.`));
|
|
1634
1693
|
process.exitCode = 1;
|
|
1635
1694
|
return;
|
|
1636
1695
|
}
|
|
1637
1696
|
const packageType = detectPackageType(cwd, fileExists2, readFileSync3);
|
|
1638
1697
|
const targetFormat = resolveFormat(formatOption, packageType);
|
|
1639
1698
|
if (targetFormat === "js" && packageType !== "module") {
|
|
1640
|
-
log(
|
|
1699
|
+
log(chalk6.yellow('Package is not marked as "type": "module"; creating doccov.config.js may require enabling ESM.'));
|
|
1641
1700
|
}
|
|
1642
1701
|
const fileName = `doccov.config.${targetFormat}`;
|
|
1643
1702
|
const outputPath = path6.join(cwd, fileName);
|
|
1644
1703
|
if (fileExists2(outputPath)) {
|
|
1645
|
-
error(
|
|
1704
|
+
error(chalk6.red(`Cannot create ${fileName}; file already exists.`));
|
|
1646
1705
|
process.exitCode = 1;
|
|
1647
1706
|
return;
|
|
1648
1707
|
}
|
|
1649
1708
|
const template = buildTemplate(targetFormat);
|
|
1650
1709
|
writeFileSync3(outputPath, template, { encoding: "utf8" });
|
|
1651
|
-
log(
|
|
1710
|
+
log(chalk6.green(`✓ Created ${path6.relative(process.cwd(), outputPath)}`));
|
|
1652
1711
|
});
|
|
1653
1712
|
}
|
|
1654
1713
|
var isValidFormat = (value) => {
|
|
@@ -1762,16 +1821,14 @@ import * as fs5 from "node:fs";
|
|
|
1762
1821
|
import * as path7 from "node:path";
|
|
1763
1822
|
import { DocCov as DocCov3, NodeFileSystem as NodeFileSystem3, resolveTarget as resolveTarget3 } from "@doccov/sdk";
|
|
1764
1823
|
import { normalize, validateSpec } from "@openpkg-ts/spec";
|
|
1824
|
+
import chalk8 from "chalk";
|
|
1765
1825
|
// package.json
|
|
1766
|
-
var version = "0.
|
|
1767
|
-
|
|
1768
|
-
// src/commands/spec.ts
|
|
1769
|
-
import chalk7 from "chalk";
|
|
1826
|
+
var version = "0.14.0";
|
|
1770
1827
|
|
|
1771
1828
|
// src/utils/filter-options.ts
|
|
1772
1829
|
import { mergeFilters, parseListFlag } from "@doccov/sdk";
|
|
1773
|
-
import
|
|
1774
|
-
var formatList = (label, values) => `${label}: ${values.map((value) =>
|
|
1830
|
+
import chalk7 from "chalk";
|
|
1831
|
+
var formatList = (label, values) => `${label}: ${values.map((value) => chalk7.cyan(value)).join(", ")}`;
|
|
1775
1832
|
var mergeFilterOptions = (config, cliOptions) => {
|
|
1776
1833
|
const messages = [];
|
|
1777
1834
|
if (config?.include) {
|
|
@@ -1812,7 +1869,7 @@ function getArrayLength(value) {
|
|
|
1812
1869
|
function formatDiagnosticOutput(prefix, diagnostic, baseDir) {
|
|
1813
1870
|
const location = diagnostic.location;
|
|
1814
1871
|
const relativePath = location?.file ? path7.relative(baseDir, location.file) || location.file : undefined;
|
|
1815
|
-
const locationText = location && relativePath ?
|
|
1872
|
+
const locationText = location && relativePath ? chalk8.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
|
|
1816
1873
|
const locationPrefix = locationText ? `${locationText} ` : "";
|
|
1817
1874
|
return `${prefix} ${locationPrefix}${diagnostic.message}`;
|
|
1818
1875
|
}
|
|
@@ -1823,6 +1880,14 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
1823
1880
|
};
|
|
1824
1881
|
program.command("spec [entry]").description("Generate OpenPkg specification (JSON)").option("--cwd <dir>", "Working directory", process.cwd()).option("-p, --package <name>", "Target package name (for monorepos)").option("-o, --output <file>", "Output file path", "openpkg.json").option("--include <patterns>", "Include exports matching pattern (comma-separated)").option("--exclude <patterns>", "Exclude exports matching pattern (comma-separated)").option("--skip-resolve", "Skip external type resolution from node_modules").option("--max-type-depth <n>", "Maximum depth for type conversion", "20").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) => {
|
|
1825
1882
|
try {
|
|
1883
|
+
const steps = new StepProgress([
|
|
1884
|
+
{ label: "Resolved target", activeLabel: "Resolving target" },
|
|
1885
|
+
{ label: "Loaded config", activeLabel: "Loading config" },
|
|
1886
|
+
{ label: "Generated spec", activeLabel: "Generating spec" },
|
|
1887
|
+
{ label: "Validated schema", activeLabel: "Validating schema" },
|
|
1888
|
+
{ label: "Wrote output", activeLabel: "Writing output" }
|
|
1889
|
+
]);
|
|
1890
|
+
steps.start();
|
|
1826
1891
|
const fileSystem = new NodeFileSystem3(options.cwd);
|
|
1827
1892
|
const resolved = await resolveTarget3(fileSystem, {
|
|
1828
1893
|
cwd: options.cwd,
|
|
@@ -1830,135 +1895,111 @@ function registerSpecCommand(program, dependencies = {}) {
|
|
|
1830
1895
|
entry
|
|
1831
1896
|
});
|
|
1832
1897
|
const { targetDir, entryFile, packageInfo, entryPointInfo } = resolved;
|
|
1833
|
-
|
|
1834
|
-
log(chalk7.gray(`Found package at ${packageInfo.path}`));
|
|
1835
|
-
}
|
|
1836
|
-
if (!entry) {
|
|
1837
|
-
log(chalk7.gray(`Auto-detected entry point: ${entryPointInfo.path} (from ${entryPointInfo.source})`));
|
|
1838
|
-
}
|
|
1898
|
+
steps.next();
|
|
1839
1899
|
let config = null;
|
|
1840
1900
|
try {
|
|
1841
1901
|
config = await loadDocCovConfig(targetDir);
|
|
1842
|
-
if (config?.filePath) {
|
|
1843
|
-
log(chalk7.gray(`Loaded configuration from ${path7.relative(targetDir, config.filePath)}`));
|
|
1844
|
-
}
|
|
1845
1902
|
} catch (configError) {
|
|
1846
|
-
error(
|
|
1903
|
+
error(chalk8.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
|
|
1847
1904
|
process.exit(1);
|
|
1848
1905
|
}
|
|
1906
|
+
steps.next();
|
|
1849
1907
|
const cliFilters = {
|
|
1850
1908
|
include: parseListFlag(options.include),
|
|
1851
1909
|
exclude: parseListFlag(options.exclude)
|
|
1852
1910
|
};
|
|
1853
1911
|
const resolvedFilters = mergeFilterOptions(config, cliFilters);
|
|
1854
|
-
for (const message of resolvedFilters.messages) {
|
|
1855
|
-
log(chalk7.gray(`${message}`));
|
|
1856
|
-
}
|
|
1857
1912
|
const resolveExternalTypes = !options.skipResolve;
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
},
|
|
1883
|
-
generationInput
|
|
1884
|
-
} : { generationInput };
|
|
1885
|
-
result = await doccov.analyzeFileWithDiagnostics(entryFile, analyzeOptions);
|
|
1886
|
-
if (result.fromCache) {
|
|
1887
|
-
process.stdout.write(chalk7.gray(`> Using cached spec
|
|
1888
|
-
`));
|
|
1889
|
-
} else {
|
|
1890
|
-
process.stdout.write(chalk7.green(`> Generated OpenPkg spec
|
|
1891
|
-
`));
|
|
1892
|
-
}
|
|
1893
|
-
} catch (generationError) {
|
|
1894
|
-
process.stdout.write(chalk7.red(`> Failed to generate spec
|
|
1895
|
-
`));
|
|
1896
|
-
throw generationError;
|
|
1897
|
-
}
|
|
1913
|
+
const doccov = createDocCov({
|
|
1914
|
+
resolveExternalTypes,
|
|
1915
|
+
maxDepth: options.maxTypeDepth ? parseInt(options.maxTypeDepth, 10) : undefined,
|
|
1916
|
+
useCache: options.cache !== false,
|
|
1917
|
+
cwd: options.cwd
|
|
1918
|
+
});
|
|
1919
|
+
const generationInput = {
|
|
1920
|
+
entryPoint: path7.relative(targetDir, entryFile),
|
|
1921
|
+
entryPointSource: entryPointInfo.source,
|
|
1922
|
+
isDeclarationOnly: entryPointInfo.isDeclarationOnly ?? false,
|
|
1923
|
+
generatorName: "@doccov/cli",
|
|
1924
|
+
generatorVersion: version,
|
|
1925
|
+
packageManager: packageInfo?.packageManager,
|
|
1926
|
+
isMonorepo: resolved.isMonorepo,
|
|
1927
|
+
targetPackage: packageInfo?.name
|
|
1928
|
+
};
|
|
1929
|
+
const analyzeOptions = resolvedFilters.include || resolvedFilters.exclude ? {
|
|
1930
|
+
filters: {
|
|
1931
|
+
include: resolvedFilters.include,
|
|
1932
|
+
exclude: resolvedFilters.exclude
|
|
1933
|
+
},
|
|
1934
|
+
generationInput
|
|
1935
|
+
} : { generationInput };
|
|
1936
|
+
const result = await doccov.analyzeFileWithDiagnostics(entryFile, analyzeOptions);
|
|
1898
1937
|
if (!result) {
|
|
1899
1938
|
throw new Error("Failed to produce an OpenPkg spec.");
|
|
1900
1939
|
}
|
|
1940
|
+
steps.next();
|
|
1901
1941
|
const normalized = normalize(result.spec);
|
|
1902
1942
|
const validation = validateSpec(normalized);
|
|
1903
1943
|
if (!validation.ok) {
|
|
1904
|
-
error(
|
|
1944
|
+
error(chalk8.red("Spec failed schema validation"));
|
|
1905
1945
|
for (const err of validation.errors) {
|
|
1906
|
-
error(
|
|
1946
|
+
error(chalk8.red(`schema: ${err.instancePath || "/"} ${err.message}`));
|
|
1907
1947
|
}
|
|
1908
1948
|
process.exit(1);
|
|
1909
1949
|
}
|
|
1950
|
+
steps.next();
|
|
1910
1951
|
const outputPath = path7.resolve(process.cwd(), options.output);
|
|
1911
1952
|
writeFileSync4(outputPath, JSON.stringify(normalized, null, 2));
|
|
1912
|
-
|
|
1913
|
-
log(
|
|
1914
|
-
log(
|
|
1953
|
+
steps.complete(`Generated ${options.output}`);
|
|
1954
|
+
log(chalk8.gray(` ${getArrayLength(normalized.exports)} exports`));
|
|
1955
|
+
log(chalk8.gray(` ${getArrayLength(normalized.types)} types`));
|
|
1915
1956
|
if (options.verbose && normalized.generation) {
|
|
1916
1957
|
const gen = normalized.generation;
|
|
1917
1958
|
log("");
|
|
1918
|
-
log(
|
|
1919
|
-
log(
|
|
1920
|
-
log(
|
|
1921
|
-
log(
|
|
1922
|
-
log(
|
|
1923
|
-
log(
|
|
1924
|
-
log(
|
|
1959
|
+
log(chalk8.bold("Generation Info"));
|
|
1960
|
+
log(chalk8.gray(` Timestamp: ${gen.timestamp}`));
|
|
1961
|
+
log(chalk8.gray(` Generator: ${gen.generator.name}@${gen.generator.version}`));
|
|
1962
|
+
log(chalk8.gray(` Entry point: ${gen.analysis.entryPoint}`));
|
|
1963
|
+
log(chalk8.gray(` Detected via: ${gen.analysis.entryPointSource}`));
|
|
1964
|
+
log(chalk8.gray(` Declaration only: ${gen.analysis.isDeclarationOnly ? "yes" : "no"}`));
|
|
1965
|
+
log(chalk8.gray(` External types: ${gen.analysis.resolvedExternalTypes ? "resolved" : "skipped"}`));
|
|
1925
1966
|
if (gen.analysis.maxTypeDepth) {
|
|
1926
|
-
log(
|
|
1967
|
+
log(chalk8.gray(` Max type depth: ${gen.analysis.maxTypeDepth}`));
|
|
1927
1968
|
}
|
|
1928
1969
|
log("");
|
|
1929
|
-
log(
|
|
1930
|
-
log(
|
|
1970
|
+
log(chalk8.bold("Environment"));
|
|
1971
|
+
log(chalk8.gray(` node_modules: ${gen.environment.hasNodeModules ? "found" : "not found"}`));
|
|
1931
1972
|
if (gen.environment.packageManager) {
|
|
1932
|
-
log(
|
|
1973
|
+
log(chalk8.gray(` Package manager: ${gen.environment.packageManager}`));
|
|
1933
1974
|
}
|
|
1934
1975
|
if (gen.environment.isMonorepo) {
|
|
1935
|
-
log(
|
|
1976
|
+
log(chalk8.gray(` Monorepo: yes`));
|
|
1936
1977
|
}
|
|
1937
1978
|
if (gen.environment.targetPackage) {
|
|
1938
|
-
log(
|
|
1979
|
+
log(chalk8.gray(` Target package: ${gen.environment.targetPackage}`));
|
|
1939
1980
|
}
|
|
1940
1981
|
if (gen.issues.length > 0) {
|
|
1941
1982
|
log("");
|
|
1942
|
-
log(
|
|
1983
|
+
log(chalk8.bold("Issues"));
|
|
1943
1984
|
for (const issue of gen.issues) {
|
|
1944
|
-
const prefix = issue.severity === "error" ?
|
|
1985
|
+
const prefix = issue.severity === "error" ? chalk8.red(">") : issue.severity === "warning" ? chalk8.yellow(">") : chalk8.cyan(">");
|
|
1945
1986
|
log(`${prefix} [${issue.code}] ${issue.message}`);
|
|
1946
1987
|
if (issue.suggestion) {
|
|
1947
|
-
log(
|
|
1988
|
+
log(chalk8.gray(` ${issue.suggestion}`));
|
|
1948
1989
|
}
|
|
1949
1990
|
}
|
|
1950
1991
|
}
|
|
1951
1992
|
}
|
|
1952
1993
|
if (options.showDiagnostics && result.diagnostics.length > 0) {
|
|
1953
1994
|
log("");
|
|
1954
|
-
log(
|
|
1995
|
+
log(chalk8.bold("Diagnostics"));
|
|
1955
1996
|
for (const diagnostic of result.diagnostics) {
|
|
1956
|
-
const prefix = diagnostic.severity === "error" ?
|
|
1997
|
+
const prefix = diagnostic.severity === "error" ? chalk8.red(">") : diagnostic.severity === "warning" ? chalk8.yellow(">") : chalk8.cyan(">");
|
|
1957
1998
|
log(formatDiagnosticOutput(prefix, diagnostic, targetDir));
|
|
1958
1999
|
}
|
|
1959
2000
|
}
|
|
1960
2001
|
} catch (commandError) {
|
|
1961
|
-
error(
|
|
2002
|
+
error(chalk8.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
|
|
1962
2003
|
process.exit(1);
|
|
1963
2004
|
}
|
|
1964
2005
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@doccov/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "DocCov CLI - Documentation coverage and drift detection for TypeScript",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -48,14 +48,13 @@
|
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@ai-sdk/anthropic": "^1.0.0",
|
|
50
50
|
"@ai-sdk/openai": "^1.0.0",
|
|
51
|
-
"@doccov/sdk": "^0.
|
|
51
|
+
"@doccov/sdk": "^0.15.0",
|
|
52
52
|
"@inquirer/prompts": "^7.8.0",
|
|
53
53
|
"@openpkg-ts/spec": "^0.9.0",
|
|
54
54
|
"ai": "^4.0.0",
|
|
55
55
|
"chalk": "^5.4.1",
|
|
56
56
|
"commander": "^14.0.0",
|
|
57
57
|
"glob": "^11.0.0",
|
|
58
|
-
"ora": "^9.0.0",
|
|
59
58
|
"simple-git": "^3.27.0",
|
|
60
59
|
"zod": "^3.25.0"
|
|
61
60
|
},
|