@driftdev/cli 0.36.0 → 0.38.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 (2) hide show
  1. package/dist/drift.js +624 -47
  2. package/package.json +1 -1
package/dist/drift.js CHANGED
@@ -175286,6 +175286,25 @@ function validateConfig(raw) {
175286
175286
  if (docs.exclude !== undefined && (!Array.isArray(docs.exclude) || !docs.exclude.every((i) => typeof i === "string"))) {
175287
175287
  errors.push('"docs.exclude" must be an array of strings');
175288
175288
  }
175289
+ if (docs.remote !== undefined) {
175290
+ if (!Array.isArray(docs.remote)) {
175291
+ errors.push('"docs.remote" must be an array');
175292
+ } else {
175293
+ for (let i = 0;i < docs.remote.length; i++) {
175294
+ const target = docs.remote[i];
175295
+ if (typeof target !== "object" || target === null) {
175296
+ errors.push(`"docs.remote[${i}]" must be an object`);
175297
+ continue;
175298
+ }
175299
+ if (typeof target.repo !== "string" || !/^[^/]+\/[^/]+$/.test(target.repo)) {
175300
+ errors.push(`"docs.remote[${i}].repo" must be in "owner/repo" format`);
175301
+ }
175302
+ if (target.branch !== undefined && typeof target.branch !== "string") {
175303
+ errors.push(`"docs.remote[${i}].branch" must be a string`);
175304
+ }
175305
+ }
175306
+ }
175307
+ }
175289
175308
  }
175290
175309
  }
175291
175310
  if (errors.length > 0)
@@ -175651,14 +175670,17 @@ function renderBatchLint(data) {
175651
175670
  function renderBatchList(data) {
175652
175671
  const lines = [""];
175653
175672
  const rows = data.packages;
175673
+ const columnLabel = data.filter === "undocumented" ? "UNDOCUMENTED" : "EXPORTS";
175654
175674
  const nameW = Math.max(7, ...rows.map((r) => r.name.length));
175655
- lines.push(indent(`${c.gray(pad("PACKAGE", nameW))} ${c.gray("EXPORTS")}`));
175675
+ lines.push(indent(`${c.gray(pad("PACKAGE", nameW))} ${c.gray(columnLabel)}`));
175656
175676
  for (const r of rows) {
175657
- lines.push(indent(`${pad(r.name, nameW)} ${r.count}`));
175677
+ const countStr = data.filter === "undocumented" ? r.count === 0 ? c.green("0") : c.yellow(String(r.count)) : String(r.count);
175678
+ lines.push(indent(`${pad(r.name, nameW)} ${countStr}`));
175658
175679
  }
175659
175680
  const total = rows.reduce((s, r) => s + r.count, 0);
175660
175681
  lines.push("");
175661
- lines.push(indent(`${c.bold("Total")}: ${total} exports across ${rows.length} packages`));
175682
+ const label = data.filter === "undocumented" ? "undocumented exports" : "exports";
175683
+ lines.push(indent(`${c.bold("Total")}: ${total} ${label} across ${rows.length} packages`));
175662
175684
  lines.push("");
175663
175685
  return lines.join(`
175664
175686
  `);
@@ -175681,6 +175703,54 @@ function renderBatchExamples(data) {
175681
175703
  return lines.join(`
175682
175704
  `);
175683
175705
  }
175706
+ function renderBatchDiff(data) {
175707
+ const lines = [""];
175708
+ const rows = data.packages;
175709
+ const nameW = Math.max(7, ...rows.map((r) => r.name.length));
175710
+ lines.push(indent(`${c.gray(pad("PACKAGE", nameW))} ${c.gray(pad("BREAKING", 8))} ${c.gray(pad("ADDED", 5))} ${c.gray("CHANGED")}`));
175711
+ for (const r of rows) {
175712
+ const bStr = pad(String(r.breaking), 8);
175713
+ const aStr = pad(String(r.added), 5);
175714
+ const cStr = String(r.changed);
175715
+ const breaking = r.breaking === 0 ? c.green(bStr) : c.red(bStr);
175716
+ const added = r.added === 0 ? aStr : c.green(aStr);
175717
+ const changed = r.changed === 0 ? cStr : c.yellow(cStr);
175718
+ lines.push(indent(`${pad(r.name, nameW)} ${breaking} ${added} ${changed}`));
175719
+ }
175720
+ lines.push("");
175721
+ const parts = [];
175722
+ if (data.aggregate.breaking > 0)
175723
+ parts.push(`${data.aggregate.breaking} breaking`);
175724
+ if (data.aggregate.added > 0)
175725
+ parts.push(`${data.aggregate.added} added`);
175726
+ if (data.aggregate.changed > 0)
175727
+ parts.push(`${data.aggregate.changed} changed`);
175728
+ lines.push(indent(`${c.bold("Total")}: ${parts.length > 0 ? parts.join(", ") : "no changes"}`));
175729
+ if (data.skipped && data.skipped.length > 0) {
175730
+ lines.push(indent(`${c.gray(`Skipped ${data.skipped.length} private: ${data.skipped.join(", ")}`)}`));
175731
+ }
175732
+ lines.push("");
175733
+ return lines.join(`
175734
+ `);
175735
+ }
175736
+ function renderBatchBreaking(data) {
175737
+ const lines = [""];
175738
+ const rows = data.packages;
175739
+ const nameW = Math.max(7, ...rows.map((r) => r.name.length));
175740
+ lines.push(indent(`${c.gray(pad("PACKAGE", nameW))} ${c.gray("BREAKING")}`));
175741
+ for (const r of rows) {
175742
+ const count = r.count === 0 ? c.green("0") : c.red(String(r.count));
175743
+ lines.push(indent(`${pad(r.name, nameW)} ${count}`));
175744
+ }
175745
+ lines.push("");
175746
+ lines.push(indent(`${c.bold("Total")}: ${data.aggregate.count} breaking change${data.aggregate.count === 1 ? "" : "s"}`));
175747
+ if (data.skipped && data.skipped.length > 0) {
175748
+ lines.push(indent(`${c.gray(`Skipped ${data.skipped.length} private: ${data.skipped.join(", ")}`)}`));
175749
+ }
175750
+ lines.push("");
175751
+ return lines.join(`
175752
+ `);
175753
+ }
175684
175754
  function pad(s, w) {
175685
175755
  return s + " ".repeat(Math.max(0, w - s.length));
175686
175756
  }
@@ -176013,7 +176083,7 @@ function getPackageInfo(cwd) {
176013
176083
  }
176014
176084
  }
176015
176085
  function registerHealthCommand(program) {
176016
- program.command("health [entry]", { isDefault: true }).description("Show documentation health score (default command)").option("--min <n>", "Minimum health threshold (exit 1 if below)").option("--all", "Run across all workspace packages").option("--private", "Include private packages in --all mode").action(async (entry, options) => {
176086
+ program.command("health [entry]").description("Show documentation health score (default command)").option("--min <n>", "Minimum health threshold (exit 1 if below)").option("--all", "Run across all workspace packages").option("--private", "Include private packages in --all mode").action(async (entry, options) => {
176017
176087
  const startTime = Date.now();
176018
176088
  const version = getVersion();
176019
176089
  try {
@@ -176105,7 +176175,7 @@ function registerHealthCommand(program) {
176105
176175
  import { readFileSync as readFileSync15 } from "node:fs";
176106
176176
  import * as path19 from "node:path";
176107
176177
  import { fileURLToPath as fileURLToPath2 } from "node:url";
176108
- import { diffSpec as diffSpec3, categorizeBreakingChanges as categorizeBreakingChanges2 } from "@openpkg-ts/spec";
176178
+ import { diffSpec as diffSpec3, categorizeBreakingChanges as categorizeBreakingChanges2, normalize as normalize4 } from "@openpkg-ts/spec";
176109
176179
 
176110
176180
  // src/formatters/breaking.ts
176111
176181
  function renderBreaking(data) {
@@ -176128,17 +176198,20 @@ function renderBreaking(data) {
176128
176198
  `);
176129
176199
  }
176130
176200
 
176131
- // src/utils/resolve-specs.ts
176132
- import { readFileSync as readFileSync14 } from "node:fs";
176133
- import * as path18 from "node:path";
176134
- import { normalize as normalize3 } from "@openpkg-ts/spec";
176135
-
176136
176201
  // src/utils/git-extract.ts
176137
176202
  import { execSync } from "node:child_process";
176138
176203
  import { existsSync as existsSync14, mkdirSync as mkdirSync4, mkdtempSync, rmSync as rmSync2, symlinkSync, writeFileSync as writeFileSync4, cpSync } from "node:fs";
176139
176204
  import * as os4 from "node:os";
176140
176205
  import * as path17 from "node:path";
176141
176206
  import { normalize as normalize2 } from "@openpkg-ts/spec";
176207
+ function getGitRoot(cwd) {
176208
+ return execSync("git rev-parse --show-toplevel", {
176209
+ encoding: "utf-8",
176210
+ cwd,
176211
+ timeout: 5000,
176212
+ stdio: ["pipe", "pipe", "pipe"]
176213
+ }).trim();
176214
+ }
176142
176215
  function listFilesAtRef(ref, prefix, cwd) {
176143
176216
  try {
176144
176217
  const output = execSync(`git ls-tree -r --name-only ${ref} -- ${prefix}`, {
@@ -176178,19 +176251,21 @@ function validateRef(ref, cwd = process.cwd()) {
176178
176251
  }
176179
176252
  }
176180
176253
  async function extractSpecFromRef(ref, entry, cwd = process.cwd()) {
176181
- const relEntry = path17.relative(cwd, path17.resolve(cwd, entry));
176182
- const entryDir = path17.dirname(relEntry);
176254
+ const gitRoot = getGitRoot(cwd);
176255
+ const absEntry = path17.resolve(cwd, entry);
176256
+ const repoRelEntry = path17.relative(gitRoot, absEntry);
176257
+ const entryDir = path17.dirname(repoRelEntry);
176183
176258
  const srcPrefix = entryDir.includes("/") ? entryDir.split("/").slice(0, 2).join("/") : ".";
176184
176259
  const tmpDir = mkdtempSync(path17.join(os4.tmpdir(), "drift-git-"));
176185
176260
  try {
176186
176261
  const prefixes = srcPrefix === "." ? [""] : [srcPrefix];
176187
176262
  const rootFiles = ["tsconfig.json", "tsconfig.base.json", "package.json"];
176188
176263
  for (const prefix of prefixes) {
176189
- const files = listFilesAtRef(ref, prefix || ".", cwd);
176264
+ const files = listFilesAtRef(ref, prefix || ".", gitRoot);
176190
176265
  for (const file of files) {
176191
176266
  if (!file.match(/\.(ts|tsx|json|js|mjs|cjs)$/))
176192
176267
  continue;
176193
- const content = getFileAtRef(ref, file, cwd);
176268
+ const content = getFileAtRef(ref, file, gitRoot);
176194
176269
  if (content === null)
176195
176270
  continue;
176196
176271
  const destPath = path17.join(tmpDir, file);
@@ -176201,7 +176276,7 @@ async function extractSpecFromRef(ref, entry, cwd = process.cwd()) {
176201
176276
  for (const rootFile of rootFiles) {
176202
176277
  const dest = path17.join(tmpDir, rootFile);
176203
176278
  if (!existsSync14(dest)) {
176204
- const content = getFileAtRef(ref, rootFile, cwd);
176279
+ const content = getFileAtRef(ref, rootFile, gitRoot);
176205
176280
  if (content) {
176206
176281
  writeFileSync4(dest, content);
176207
176282
  }
@@ -176212,8 +176287,15 @@ async function extractSpecFromRef(ref, entry, cwd = process.cwd()) {
176212
176287
  if (existsSync14(nmSrc) && !existsSync14(nmDest)) {
176213
176288
  symlinkSync(nmSrc, nmDest, "dir");
176214
176289
  }
176290
+ if (cwd !== gitRoot) {
176291
+ const rootNm = path17.join(gitRoot, "node_modules");
176292
+ const rootNmDest = path17.join(tmpDir, "node_modules");
176293
+ if (existsSync14(rootNm) && !existsSync14(rootNmDest)) {
176294
+ symlinkSync(rootNm, rootNmDest, "dir");
176295
+ }
176296
+ }
176215
176297
  if (srcPrefix !== ".") {
176216
- const nestedNm = path17.join(cwd, srcPrefix, "node_modules");
176298
+ const nestedNm = path17.join(gitRoot, srcPrefix, "node_modules");
176217
176299
  const nestedDest = path17.join(tmpDir, srcPrefix, "node_modules");
176218
176300
  if (existsSync14(nestedNm) && !existsSync14(nestedDest)) {
176219
176301
  symlinkSync(nestedNm, nestedDest, "dir");
@@ -176224,9 +176306,9 @@ async function extractSpecFromRef(ref, entry, cwd = process.cwd()) {
176224
176306
  if (existsSync14(tsconfigSrc) && !existsSync14(tsconfigDest)) {
176225
176307
  cpSync(tsconfigSrc, tsconfigDest);
176226
176308
  }
176227
- const entryFile = path17.join(tmpDir, relEntry);
176309
+ const entryFile = path17.join(tmpDir, repoRelEntry);
176228
176310
  if (!existsSync14(entryFile)) {
176229
- throw new Error(`Entry file not found at ref ${ref}: ${relEntry}`);
176311
+ throw new Error(`Entry file not found at ref ${ref}: ${repoRelEntry}`);
176230
176312
  }
176231
176313
  const result = await extract({ entryFile });
176232
176314
  return normalize2(result.spec);
@@ -176236,6 +176318,9 @@ async function extractSpecFromRef(ref, entry, cwd = process.cwd()) {
176236
176318
  }
176237
176319
 
176238
176320
  // src/utils/resolve-specs.ts
176321
+ import { readFileSync as readFileSync14 } from "node:fs";
176322
+ import * as path18 from "node:path";
176323
+ import { normalize as normalize3 } from "@openpkg-ts/spec";
176239
176324
  function loadSpec(filePath) {
176240
176325
  return JSON.parse(readFileSync14(path18.resolve(process.cwd(), filePath), "utf-8"));
176241
176326
  }
@@ -176294,10 +176379,52 @@ function getVersion2() {
176294
176379
  }
176295
176380
  }
176296
176381
  function registerBreakingCommand(program) {
176297
- program.command("breaking [old] [new]").description("Detect breaking changes between two specs").option("--base <ref>", "Git ref for old spec").option("--head <ref>", "Git ref for new spec (default: working tree)").option("--entry <file>", "Entry file for git ref extraction").action(async (oldPath, newPath, options) => {
176382
+ program.command("breaking [old] [new]").description("Detect breaking changes between two specs").option("--base <ref>", "Git ref for old spec").option("--head <ref>", "Git ref for new spec (default: working tree)").option("--entry <file>", "Entry file for git ref extraction").option("--all", "Run across all workspace packages").option("--private", "Include private packages in --all mode").action(async (oldPath, newPath, options) => {
176298
176383
  const startTime = Date.now();
176299
176384
  const version = getVersion2();
176300
176385
  try {
176386
+ const cwd = process.cwd();
176387
+ const allPackages = options.all ? discoverPackages(cwd) : null;
176388
+ const autoDetected = !options.all && options.base && !options.entry && !oldPath ? discoverPackages(cwd) : null;
176389
+ const batchPackages = allPackages ?? autoDetected;
176390
+ if (batchPackages && batchPackages.length > 0 && options.base) {
176391
+ const skipped = options.private ? [] : batchPackages.filter((p) => p.private).map((p) => p.name);
176392
+ const packages = options.private ? batchPackages : filterPublic(batchPackages);
176393
+ if (packages.length === 0) {
176394
+ formatError("breaking", "No workspace packages found", startTime, version);
176395
+ return;
176396
+ }
176397
+ const rows = [];
176398
+ let totalBreaking = 0;
176399
+ for (const pkg of packages) {
176400
+ const relEntry = path19.relative(cwd, pkg.entry);
176401
+ const oldSpec2 = await extractSpecFromRef(options.base, relEntry, cwd);
176402
+ let newSpec2;
176403
+ if (options.head) {
176404
+ newSpec2 = await extractSpecFromRef(options.head, relEntry, cwd);
176405
+ } else {
176406
+ const result = await extract({ entryFile: pkg.entry });
176407
+ newSpec2 = normalize4(result.spec);
176408
+ }
176409
+ const diff2 = diffSpec3(oldSpec2, newSpec2);
176410
+ const breaking2 = categorizeBreakingChanges2(diff2.breaking, oldSpec2, newSpec2);
176411
+ rows.push({ name: pkg.name, breaking: breaking2, count: breaking2.length });
176412
+ totalBreaking += breaking2.length;
176413
+ }
176414
+ const data2 = { packages: rows, aggregate: { count: totalBreaking }, ...skipped.length > 0 ? { skipped } : {} };
176415
+ formatOutput("breaking", data2, startTime, version, renderBatchBreaking);
176416
+ if (totalBreaking > 0) {
176417
+ if (!shouldRenderHuman()) {
176418
+ process.stderr.write(`${totalBreaking} breaking change${totalBreaking === 1 ? "" : "s"} found
176419
+ `);
176420
+ }
176421
+ process.exitCode = 1;
176422
+ } else if (!shouldRenderHuman()) {
176423
+ process.stderr.write(`No breaking changes
176424
+ `);
176425
+ }
176426
+ return;
176427
+ }
176301
176428
  const args = [oldPath, newPath].filter(Boolean);
176302
176429
  const { oldSpec, newSpec } = await resolveSpecs({ args, ...options });
176303
176430
  const diff = diffSpec3(oldSpec, newSpec);
@@ -176517,6 +176644,7 @@ import { existsSync as existsSync17, readFileSync as readFileSync20 } from "node
176517
176644
  import * as path24 from "node:path";
176518
176645
  import { fileURLToPath as fileURLToPath5 } from "node:url";
176519
176646
  import { computeDrift as computeDrift2 } from "@driftdev/sdk";
176647
+ import { diffSpec as diffSpec6, categorizeBreakingChanges as categorizeBreakingChanges4 } from "@openpkg-ts/spec";
176520
176648
 
176521
176649
  // src/formatters/ci.ts
176522
176650
  function renderCi(data) {
@@ -176771,25 +176899,109 @@ function filterChangedPackages(allDirs, changedFiles) {
176771
176899
  return changedFiles.some((f) => prefix === "" || f.startsWith(prefix));
176772
176900
  });
176773
176901
  }
176774
- function buildMarkdownTable(results, pass) {
176902
+ function buildPRComment(results, pass, commit) {
176775
176903
  const lines = [];
176776
- lines.push(`## Drift CI Results
176904
+ const multi = results.length > 1;
176905
+ lines.push(`## Drift CI
176777
176906
  `);
176778
176907
  lines.push("| Package | Exports | Coverage | Lint | Status |");
176779
176908
  lines.push("|---------|---------|----------|------|--------|");
176780
176909
  for (const r of results) {
176781
- const cov = r.coveragePass ? `${r.coverage}%` : `${r.coverage}% ❌`;
176782
- const lint = r.lintPass ? `${r.lintIssues} issues` : `${r.lintIssues} issues ❌`;
176783
- const status = r.pass ? "" : "";
176910
+ const cov = r.coveragePass ? `${r.coverage}%` : `${r.coverage}% :x:`;
176911
+ const lint = r.lintPass ? `${r.lintIssues}` : `${r.lintIssues} :x:`;
176912
+ const status = r.pass ? ":white_check_mark:" : ":x:";
176784
176913
  lines.push(`| ${r.name} | ${r.exports} | ${cov} | ${lint} | ${status} |`);
176785
176914
  }
176915
+ const apiEntries = [];
176916
+ for (const r of results) {
176917
+ if (!r.diff)
176918
+ continue;
176919
+ const { breaking, added, changed } = r.diff;
176920
+ if (breaking.length === 0 && added.length === 0 && changed.length === 0)
176921
+ continue;
176922
+ const pkgLines = [];
176923
+ if (multi)
176924
+ pkgLines.push(`
176925
+ **${r.name}**`);
176926
+ for (const name of added)
176927
+ pkgLines.push(`- :heavy_plus_sign: Added: \`${name}\``);
176928
+ for (const name of changed)
176929
+ pkgLines.push(`- :pencil2: Changed: \`${name}\``);
176930
+ for (const b of breaking)
176931
+ pkgLines.push(`- :x: Removed: \`${b.name}\``);
176932
+ apiEntries.push(pkgLines.join(`
176933
+ `));
176934
+ }
176935
+ if (apiEntries.length > 0) {
176936
+ const total = results.reduce((s, r) => s + (r.diff ? r.diff.breaking.length + r.diff.added.length + r.diff.changed.length : 0), 0);
176937
+ lines.push("");
176938
+ lines.push(`<details>
176939
+ <summary>API Changes (${total})</summary>
176940
+ `);
176941
+ lines.push(apiEntries.join(`
176942
+ `));
176943
+ lines.push(`
176944
+ </details>`);
176945
+ }
176946
+ const breakingEntries = [];
176947
+ for (const r of results) {
176948
+ if (!r.diff?.breaking.length)
176949
+ continue;
176950
+ const pkgLines = [];
176951
+ if (multi)
176952
+ pkgLines.push(`
176953
+ **${r.name}**`);
176954
+ for (const b of r.diff.breaking) {
176955
+ pkgLines.push(`- \`${b.name}\`${b.reason ? `: ${b.reason}` : ""}`);
176956
+ }
176957
+ breakingEntries.push(pkgLines.join(`
176958
+ `));
176959
+ }
176960
+ if (breakingEntries.length > 0) {
176961
+ const total = results.reduce((s, r) => s + (r.diff?.breaking.length ?? 0), 0);
176962
+ lines.push("");
176963
+ lines.push(`<details>
176964
+ <summary>Breaking Changes (${total})</summary>
176965
+ `);
176966
+ lines.push(breakingEntries.join(`
176967
+ `));
176968
+ lines.push(`
176969
+ </details>`);
176970
+ }
176971
+ const undocEntries = [];
176972
+ for (const r of results) {
176973
+ if (!r.undocumented?.length)
176974
+ continue;
176975
+ const pkgLines = [];
176976
+ if (multi)
176977
+ pkgLines.push(`
176978
+ **${r.name}**`);
176979
+ for (const name of r.undocumented)
176980
+ pkgLines.push(`- \`${name}\``);
176981
+ undocEntries.push(pkgLines.join(`
176982
+ `));
176983
+ }
176984
+ if (undocEntries.length > 0) {
176985
+ const total = results.reduce((s, r) => s + (r.undocumented?.length ?? 0), 0);
176986
+ lines.push("");
176987
+ lines.push(`<details>
176988
+ <summary>Undocumented Exports (${total})</summary>
176989
+ `);
176990
+ lines.push(undocEntries.join(`
176991
+ `));
176992
+ lines.push(`
176993
+ </details>`);
176994
+ }
176786
176995
  lines.push("");
176787
- lines.push(pass ? "**All checks passed.**" : "**Some checks failed.**");
176996
+ lines.push("---");
176997
+ const ts18 = new Date().toISOString();
176998
+ const sha = commit ?? "";
176999
+ lines.push(`*[Drift](https://github.com/driftdev/drift) · ${ts18}${sha ? ` · ${sha}` : ""}*`);
176788
177000
  return lines.join(`
176789
177001
  `);
176790
177002
  }
176791
177003
  function registerCiCommand(program) {
176792
- program.command("ci").description("Run CI checks on changed packages").option("--all", "Check all packages, not just changed ones").option("--private", "Include private packages").action(async (options) => {
177004
+ program.command("ci").description("Run CI checks on changed packages").option("--all", "Check all packages, not just changed ones").option("--private", "Include private packages").option("--min <number>", "Minimum coverage percentage (0-100)").action(async (options) => {
176793
177005
  const startTime = Date.now();
176794
177006
  const version = getVersion5();
176795
177007
  const cwd = process.cwd();
@@ -176806,7 +177018,7 @@ function registerCiCommand(program) {
176806
177018
  }
176807
177019
  if (packageDirs.length === 0)
176808
177020
  packageDirs = allDirs;
176809
- let minThreshold = config.coverage?.min ?? 0;
177021
+ let minThreshold = options.min ? Number(options.min) : config.coverage?.min ?? 0;
176810
177022
  if (minThreshold > 0 && config.coverage?.ratchet) {
176811
177023
  const ratchet = computeRatchetMin(minThreshold);
176812
177024
  minThreshold = ratchet.effectiveMin;
@@ -176852,6 +177064,24 @@ function registerCiCommand(program) {
176852
177064
  lintIssues += drifts.length;
176853
177065
  }
176854
177066
  const lintPass = config.lint === false || lintIssues === 0;
177067
+ let diff;
177068
+ let undocumented;
177069
+ if (gh.isPR && gh.baseRef) {
177070
+ try {
177071
+ const relEntry = path24.relative(cwd, detectEntry(absDir));
177072
+ const oldSpec = await extractSpecFromRef(`origin/${gh.baseRef}`, relEntry, cwd);
177073
+ const diffResult = diffSpec6(oldSpec, spec);
177074
+ const breaking = categorizeBreakingChanges4(diffResult.breaking, oldSpec, spec);
177075
+ diff = {
177076
+ breaking: breaking.map((b) => ({ name: b.name, reason: b.reason })),
177077
+ added: diffResult.nonBreaking,
177078
+ changed: diffResult.docsOnly
177079
+ };
177080
+ } catch {}
177081
+ const undoc = exports.filter((e) => !e.description?.trim()).map((e) => e.name);
177082
+ if (undoc.length > 0)
177083
+ undocumented = undoc;
177084
+ }
176855
177085
  results.push({
176856
177086
  name,
176857
177087
  coverage,
@@ -176859,7 +177089,9 @@ function registerCiCommand(program) {
176859
177089
  lintIssues,
176860
177090
  lintPass,
176861
177091
  exports: total,
176862
- pass: coveragePass && lintPass
177092
+ pass: coveragePass && lintPass,
177093
+ diff,
177094
+ undocumented
176863
177095
  });
176864
177096
  } catch {
176865
177097
  results.push({
@@ -176897,7 +177129,7 @@ function registerCiCommand(program) {
176897
177129
  });
176898
177130
  } catch {}
176899
177131
  if (gh.isPR && gh.token && gh.repository) {
176900
- const md = buildMarkdownTable(results, allPass);
177132
+ const md = buildPRComment(results, allPass, commit);
176901
177133
  writeStepSummary(md);
176902
177134
  const prNumber = getPRNumber(gh.eventPath);
176903
177135
  if (prNumber) {
@@ -177244,7 +177476,7 @@ function registerExamplesCommand(program) {
177244
177476
  import { readFileSync as readFileSync23 } from "node:fs";
177245
177477
  import * as path27 from "node:path";
177246
177478
  import { fileURLToPath as fileURLToPath8 } from "node:url";
177247
- import { diffSpec as diffSpec6, categorizeBreakingChanges as categorizeBreakingChanges4 } from "@openpkg-ts/spec";
177479
+ import { diffSpec as diffSpec7, categorizeBreakingChanges as categorizeBreakingChanges5, normalize as normalize5 } from "@openpkg-ts/spec";
177248
177480
 
177249
177481
  // src/formatters/diff.ts
177250
177482
  function renderDiff(data) {
@@ -177304,14 +177536,62 @@ function getVersion8() {
177304
177536
  }
177305
177537
  }
177306
177538
  function registerDiffCommand(program) {
177307
- program.command("diff [old] [new]").description("Compare two specs and show what changed").option("--base <ref>", "Git ref for old spec").option("--head <ref>", "Git ref for new spec (default: working tree)").option("--entry <file>", "Entry file for git ref extraction").action(async (oldPath, newPath, options) => {
177539
+ program.command("diff [old] [new]").description("Compare two specs and show what changed").option("--base <ref>", "Git ref for old spec").option("--head <ref>", "Git ref for new spec (default: working tree)").option("--entry <file>", "Entry file for git ref extraction").option("--all", "Run across all workspace packages").option("--private", "Include private packages in --all mode").action(async (oldPath, newPath, options) => {
177308
177540
  const startTime = Date.now();
177309
177541
  const version = getVersion8();
177310
177542
  try {
177543
+ const cwd = process.cwd();
177544
+ const allPackages = options.all ? discoverPackages(cwd) : null;
177545
+ const autoDetected = !options.all && options.base && !options.entry && !oldPath ? discoverPackages(cwd) : null;
177546
+ const batchPackages = allPackages ?? autoDetected;
177547
+ if (batchPackages && batchPackages.length > 0 && options.base) {
177548
+ const skipped = options.private ? [] : batchPackages.filter((p) => p.private).map((p) => p.name);
177549
+ const packages = options.private ? batchPackages : filterPublic(batchPackages);
177550
+ if (packages.length === 0) {
177551
+ formatError("diff", "No workspace packages found", startTime, version);
177552
+ return;
177553
+ }
177554
+ const rows = [];
177555
+ let totalBreaking = 0;
177556
+ let totalAdded = 0;
177557
+ let totalChanged = 0;
177558
+ for (const pkg of packages) {
177559
+ const relEntry = path27.relative(cwd, pkg.entry);
177560
+ const oldSpec2 = await extractSpecFromRef(options.base, relEntry, cwd);
177561
+ let newSpec2;
177562
+ if (options.head) {
177563
+ newSpec2 = await extractSpecFromRef(options.head, relEntry, cwd);
177564
+ } else {
177565
+ const result = await extract({ entryFile: pkg.entry });
177566
+ newSpec2 = normalize5(result.spec);
177567
+ }
177568
+ const diff2 = diffSpec7(oldSpec2, newSpec2);
177569
+ rows.push({ name: pkg.name, breaking: diff2.breaking.length, added: diff2.nonBreaking.length, changed: diff2.docsOnly.length });
177570
+ totalBreaking += diff2.breaking.length;
177571
+ totalAdded += diff2.nonBreaking.length;
177572
+ totalChanged += diff2.docsOnly.length;
177573
+ }
177574
+ const data2 = { packages: rows, aggregate: { breaking: totalBreaking, added: totalAdded, changed: totalChanged }, ...skipped.length > 0 ? { skipped } : {} };
177575
+ formatOutput("diff", data2, startTime, version, renderBatchDiff);
177576
+ if (!shouldRenderHuman()) {
177577
+ const parts = [];
177578
+ if (totalBreaking > 0)
177579
+ parts.push(`${totalBreaking} breaking`);
177580
+ if (totalAdded > 0)
177581
+ parts.push(`${totalAdded} added`);
177582
+ if (totalChanged > 0)
177583
+ parts.push(`${totalChanged} changed`);
177584
+ process.stderr.write(`${parts.length > 0 ? parts.join(", ") : "no changes"}
177585
+ `);
177586
+ }
177587
+ if (totalBreaking > 0)
177588
+ process.exitCode = 1;
177589
+ return;
177590
+ }
177311
177591
  const args = [oldPath, newPath].filter(Boolean);
177312
177592
  const { oldSpec, newSpec } = await resolveSpecs({ args, ...options });
177313
- const diff = diffSpec6(oldSpec, newSpec);
177314
- const breaking = categorizeBreakingChanges4(diff.breaking, oldSpec, newSpec);
177593
+ const diff = diffSpec7(oldSpec, newSpec);
177594
+ const breaking = categorizeBreakingChanges5(diff.breaking, oldSpec, newSpec);
177315
177595
  const data = {
177316
177596
  breaking,
177317
177597
  added: diff.nonBreaking,
@@ -177347,7 +177627,7 @@ import { readFileSync as readFileSync24 } from "node:fs";
177347
177627
  import * as path28 from "node:path";
177348
177628
  import { fileURLToPath as fileURLToPath9 } from "node:url";
177349
177629
  import { DocCov } from "@driftdev/sdk";
177350
- import { normalize as normalize4 } from "@openpkg-ts/spec";
177630
+ import { normalize as normalize6 } from "@openpkg-ts/spec";
177351
177631
 
177352
177632
  // src/formatters/extract.ts
177353
177633
  function renderExtract(data) {
@@ -177415,7 +177695,7 @@ function registerExtractCommand(program) {
177415
177695
  formatError("extract", "Failed to extract spec", startTime, version);
177416
177696
  return;
177417
177697
  }
177418
- spec = normalize4(result.spec);
177698
+ spec = normalize6(result.spec);
177419
177699
  } else {
177420
177700
  const result = await cachedExtract(entryFile);
177421
177701
  spec = result.spec;
@@ -177831,7 +178111,7 @@ function flattenConfig(obj, prefix = "") {
177831
178111
  }
177832
178112
  function registerConfigCommand(program) {
177833
178113
  const cmd = program.command("config").description("Manage drift configuration");
177834
- cmd.command("list").description("Show all config values").action(() => {
178114
+ cmd.command("list").alias("show").description("Show all config values").action(() => {
177835
178115
  const startTime = Date.now();
177836
178116
  const version = getVersion12();
177837
178117
  try {
@@ -178025,17 +178305,18 @@ import { existsSync as existsSync19, mkdirSync as mkdirSync7, readFileSync as re
178025
178305
  import * as path33 from "node:path";
178026
178306
  import { fileURLToPath as fileURLToPath14 } from "node:url";
178027
178307
  init_global();
178028
- import { normalize as normalize5 } from "@openpkg-ts/spec";
178308
+ import { normalize as normalize7 } from "@openpkg-ts/spec";
178029
178309
 
178030
178310
  // src/formatters/init.ts
178031
178311
  function renderInit(data) {
178032
178312
  const lines = [""];
178033
178313
  lines.push(indent(`${data.isMonorepo ? "Monorepo" : "Project"} scan ${c.gray(`${data.packages.length} package${data.packages.length === 1 ? "" : "s"}`)}`));
178034
178314
  lines.push("");
178035
- const header = ["PACKAGE", "ENTRY", "EXPORTS", "COVERAGE"];
178315
+ const header = ["Package", "Exports", "Coverage", "Health"];
178036
178316
  const rows = data.packages.map((pkg) => {
178037
- const color = coverageColor(pkg.coverage);
178038
- return [pkg.name, pkg.entry, String(pkg.exports), color(`${pkg.coverage}%`)];
178317
+ const covColor = coverageColor(pkg.coverage);
178318
+ const healthColor = coverageColor(pkg.health);
178319
+ return [pkg.name, String(pkg.exports), covColor(`${pkg.coverage}%`), healthColor(`${pkg.health}%`)];
178039
178320
  });
178040
178321
  lines.push(indent(table([header, ...rows])));
178041
178322
  lines.push("");
@@ -178050,6 +178331,277 @@ function renderInit(data) {
178050
178331
  `);
178051
178332
  }
178052
178333
 
178334
+ // src/utils/progress/spinner.ts
178335
+ import chalk3 from "chalk";
178336
+
178337
+ // src/utils/progress/colors.ts
178338
+ import chalk2 from "chalk";
178339
+ var colors = {
178340
+ success: chalk2.green,
178341
+ error: chalk2.red,
178342
+ warning: chalk2.yellow,
178343
+ info: chalk2.cyan,
178344
+ muted: chalk2.gray,
178345
+ bold: chalk2.bold,
178346
+ dim: chalk2.dim,
178347
+ underline: chalk2.underline,
178348
+ primary: chalk2.cyan,
178349
+ secondary: chalk2.magenta,
178350
+ path: chalk2.cyan,
178351
+ number: chalk2.yellow,
178352
+ code: chalk2.gray
178353
+ };
178354
+ var symbols = {
178355
+ success: "✓",
178356
+ error: "✗",
178357
+ warning: "⚠",
178358
+ info: "ℹ",
178359
+ spinner: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
178360
+ bullet: "•",
178361
+ arrow: "→",
178362
+ arrowRight: "›",
178363
+ line: "─",
178364
+ corner: "└",
178365
+ vertical: "│",
178366
+ horizontalLine: "─"
178367
+ };
178368
+ var asciiSymbols = {
178369
+ success: "+",
178370
+ error: "x",
178371
+ warning: "!",
178372
+ info: "i",
178373
+ spinner: ["-", "\\", "|", "/"],
178374
+ bullet: "*",
178375
+ arrow: "->",
178376
+ arrowRight: ">",
178377
+ line: "-",
178378
+ corner: "\\",
178379
+ vertical: "|",
178380
+ horizontalLine: "-"
178381
+ };
178382
+ function getSymbols(unicodeSupport = true) {
178383
+ return unicodeSupport ? symbols : asciiSymbols;
178384
+ }
178385
+ var prefix = {
178386
+ success: colors.success(symbols.success),
178387
+ error: colors.error(symbols.error),
178388
+ warning: colors.warning(symbols.warning),
178389
+ info: colors.info(symbols.info)
178390
+ };
178391
+
178392
+ // src/utils/progress/utils.ts
178393
+ function isTTY2() {
178394
+ return Boolean(process.stdout.isTTY);
178395
+ }
178396
+ function isCI() {
178397
+ return Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.GITLAB_CI || process.env.CIRCLECI || process.env.TRAVIS || process.env.JENKINS_URL || process.env.BUILDKITE || process.env.TEAMCITY_VERSION || process.env.TF_BUILD || process.env.CODEBUILD_BUILD_ID || process.env.BITBUCKET_BUILD_NUMBER);
178398
+ }
178399
+ function isInteractive() {
178400
+ return isTTY2() && !isCI();
178401
+ }
178402
+ function supportsUnicode() {
178403
+ if (process.platform === "win32") {
178404
+ return Boolean(process.env.WT_SESSION) || process.env.TERM_PROGRAM === "vscode";
178405
+ }
178406
+ return process.env.TERM !== "linux";
178407
+ }
178408
+ var MIN_TERMINAL_WIDTH = 40;
178409
+ var DEFAULT_TERMINAL_WIDTH = 80;
178410
+ function getTerminalWidth() {
178411
+ const width = process.stdout.columns || DEFAULT_TERMINAL_WIDTH;
178412
+ return Math.max(width, MIN_TERMINAL_WIDTH);
178413
+ }
178414
+ var cursor = {
178415
+ hide: "\x1B[?25l",
178416
+ show: "\x1B[?25h",
178417
+ up: (n = 1) => `\x1B[${n}A`,
178418
+ down: (n = 1) => `\x1B[${n}B`,
178419
+ forward: (n = 1) => `\x1B[${n}C`,
178420
+ back: (n = 1) => `\x1B[${n}D`,
178421
+ left: "\x1B[G",
178422
+ clearLine: "\x1B[2K",
178423
+ clearDown: "\x1B[J",
178424
+ save: "\x1B[s",
178425
+ restore: "\x1B[u"
178426
+ };
178427
+ function clearLine() {
178428
+ if (isTTY2()) {
178429
+ process.stdout.write(cursor.clearLine + cursor.left);
178430
+ }
178431
+ }
178432
+ function hideCursor() {
178433
+ if (isTTY2()) {
178434
+ process.stdout.write(cursor.hide);
178435
+ }
178436
+ }
178437
+ function showCursor() {
178438
+ if (isTTY2()) {
178439
+ process.stdout.write(cursor.show);
178440
+ }
178441
+ }
178442
+ function truncate(text, maxLength) {
178443
+ if (text.length <= maxLength)
178444
+ return text;
178445
+ return `${text.slice(0, maxLength - 1)}…`;
178446
+ }
178447
+
178448
+ // src/utils/progress/spinner.ts
178449
+ var spinnerColors = {
178450
+ cyan: chalk3.cyan,
178451
+ yellow: chalk3.yellow,
178452
+ green: chalk3.green,
178453
+ red: chalk3.red,
178454
+ magenta: chalk3.magenta,
178455
+ blue: chalk3.blue,
178456
+ white: chalk3.white
178457
+ };
178458
+ var FRAME_SETS = {
178459
+ dots: ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
178460
+ circle: ["◐", "◓", "◑", "◒"]
178461
+ };
178462
+ var ASCII_FRAME_SET = ["-", "\\", "|", "/"];
178463
+
178464
+ class Spinner {
178465
+ label;
178466
+ detail;
178467
+ frames;
178468
+ interval;
178469
+ colorFn;
178470
+ frameIndex = 0;
178471
+ timer = null;
178472
+ state = "stopped";
178473
+ symbols = getSymbols(supportsUnicode());
178474
+ lastRenderedLines = 0;
178475
+ sigintHandler = null;
178476
+ constructor(options = {}) {
178477
+ this.label = options.label ?? "";
178478
+ this.detail = options.detail;
178479
+ this.interval = options.interval ?? 80;
178480
+ this.colorFn = spinnerColors[options.color ?? "cyan"];
178481
+ const style = options.style ?? "circle";
178482
+ this.frames = supportsUnicode() ? FRAME_SETS[style] : ASCII_FRAME_SET;
178483
+ }
178484
+ start(label) {
178485
+ if (label !== undefined)
178486
+ this.label = label;
178487
+ if (this.state === "spinning")
178488
+ return this;
178489
+ this.state = "spinning";
178490
+ this.frameIndex = 0;
178491
+ this.lastRenderedLines = 0;
178492
+ if (!isInteractive()) {
178493
+ console.log(`${this.symbols.bullet} ${this.label}`);
178494
+ return this;
178495
+ }
178496
+ hideCursor();
178497
+ this.setupSignalHandler();
178498
+ this.render();
178499
+ this.timer = setInterval(() => {
178500
+ this.frameIndex = (this.frameIndex + 1) % this.frames.length;
178501
+ this.render();
178502
+ }, this.interval);
178503
+ return this;
178504
+ }
178505
+ stop() {
178506
+ if (this.timer) {
178507
+ clearInterval(this.timer);
178508
+ this.timer = null;
178509
+ }
178510
+ this.state = "stopped";
178511
+ this.clearOutput();
178512
+ this.cleanup();
178513
+ return this;
178514
+ }
178515
+ success(label) {
178516
+ if (label !== undefined)
178517
+ this.label = label;
178518
+ this.finish("success");
178519
+ return this;
178520
+ }
178521
+ fail(label) {
178522
+ if (label !== undefined)
178523
+ this.label = label;
178524
+ this.finish("error");
178525
+ return this;
178526
+ }
178527
+ update(label) {
178528
+ this.label = label;
178529
+ if (this.state === "spinning" && isInteractive()) {
178530
+ this.render();
178531
+ }
178532
+ return this;
178533
+ }
178534
+ setDetail(detail) {
178535
+ this.detail = detail;
178536
+ if (this.state === "spinning" && isInteractive()) {
178537
+ this.render();
178538
+ }
178539
+ return this;
178540
+ }
178541
+ get isSpinning() {
178542
+ return this.state === "spinning";
178543
+ }
178544
+ finish(state) {
178545
+ if (this.timer) {
178546
+ clearInterval(this.timer);
178547
+ this.timer = null;
178548
+ }
178549
+ this.state = state;
178550
+ if (!isInteractive()) {
178551
+ const symbol = state === "success" ? this.symbols.success : this.symbols.error;
178552
+ const colorFn = state === "success" ? colors.success : colors.error;
178553
+ console.log(`${colorFn(symbol)} ${this.label}`);
178554
+ } else {
178555
+ this.clearOutput();
178556
+ const symbol = state === "success" ? this.symbols.success : this.symbols.error;
178557
+ const colorFn = state === "success" ? colors.success : colors.error;
178558
+ process.stdout.write(`${colorFn(symbol)} ${this.label}
178559
+ `);
178560
+ }
178561
+ this.cleanup();
178562
+ }
178563
+ render() {
178564
+ if (!isTTY2())
178565
+ return;
178566
+ this.clearOutput();
178567
+ const frame = this.colorFn(this.frames[this.frameIndex]);
178568
+ const width = getTerminalWidth();
178569
+ const mainLine = truncate(`${frame} ${this.label}`, width);
178570
+ process.stdout.write(mainLine);
178571
+ let lines = 1;
178572
+ if (this.detail) {
178573
+ const detailLine = truncate(` ${colors.muted(this.detail)}`, width);
178574
+ process.stdout.write(`
178575
+ ${detailLine}`);
178576
+ lines = 2;
178577
+ }
178578
+ this.lastRenderedLines = lines;
178579
+ }
178580
+ clearOutput() {
178581
+ if (!isTTY2())
178582
+ return;
178583
+ for (let i = 0;i < this.lastRenderedLines; i++) {
178584
+ if (i > 0)
178585
+ process.stdout.write(cursor.up(1));
178586
+ clearLine();
178587
+ }
178588
+ }
178589
+ setupSignalHandler() {
178590
+ this.sigintHandler = () => {
178591
+ this.cleanup();
178592
+ process.exit(130);
178593
+ };
178594
+ process.on("SIGINT", this.sigintHandler);
178595
+ }
178596
+ cleanup() {
178597
+ if (this.sigintHandler) {
178598
+ process.removeListener("SIGINT", this.sigintHandler);
178599
+ this.sigintHandler = null;
178600
+ }
178601
+ showCursor();
178602
+ }
178603
+ }
178604
+
178053
178605
  // src/commands/init.ts
178054
178606
  var __dirname15 = path33.dirname(fileURLToPath14(import.meta.url));
178055
178607
  function getVersion14() {
@@ -178075,7 +178627,7 @@ async function scanPackage(cwd, pkgDir) {
178075
178627
  try {
178076
178628
  const entryFile = detectEntry(absDir);
178077
178629
  const result = await extract({ entryFile });
178078
- const spec = normalize5(result.spec);
178630
+ const spec = normalize7(result.spec);
178079
178631
  const exports = spec.exports ?? [];
178080
178632
  const total = exports.length;
178081
178633
  let documented = 0;
@@ -178084,7 +178636,8 @@ async function scanPackage(cwd, pkgDir) {
178084
178636
  documented++;
178085
178637
  }
178086
178638
  const coverage = total > 0 ? Math.round(documented / total * 100) : 100;
178087
- return { name, entry: path33.relative(cwd, entryFile), exports: total, coverage };
178639
+ const health = Math.round(coverage * 0.5 + 100 * 0.5);
178640
+ return { name, entry: path33.relative(cwd, entryFile), exports: total, coverage, health };
178088
178641
  } catch {
178089
178642
  return null;
178090
178643
  }
@@ -178105,12 +178658,16 @@ function registerInitCommand(program) {
178105
178658
  const workspaces = detectWorkspaces(cwd);
178106
178659
  const isMonorepo = workspaces !== null;
178107
178660
  const packageDirs = isMonorepo ? resolveGlobs(cwd, workspaces) : ["."];
178661
+ const spin = new Spinner({ style: "dots" });
178662
+ spin.start("Scanning packages…");
178108
178663
  const packages = [];
178109
178664
  for (const dir of packageDirs) {
178665
+ spin.update(`Scanning ${dir}…`);
178110
178666
  const result = await scanPackage(cwd, dir);
178111
178667
  if (result)
178112
178668
  packages.push(result);
178113
178669
  }
178670
+ spin.success(`Scanned ${packages.length} package${packages.length === 1 ? "" : "s"}`);
178114
178671
  if (packages.length === 0) {
178115
178672
  formatError("init", "No TypeScript packages found", startTime, version);
178116
178673
  return;
@@ -178523,6 +179080,13 @@ function renderList(data) {
178523
179080
  lines.push(` ${c.bold(`${total} exports`)}`);
178524
179081
  }
178525
179082
  lines.push("");
179083
+ if (total === 0 && data.filter) {
179084
+ const msg = data.filter === "undocumented" ? `${c.green(sym.ok)} All exports are documented` : `${c.green(sym.ok)} No drifted exports found`;
179085
+ lines.push(indent(msg));
179086
+ lines.push("");
179087
+ return lines.join(`
179088
+ `);
179089
+ }
178526
179090
  if (!data.search) {
178527
179091
  const kindCounts = new Map;
178528
179092
  for (const exp of exports) {
@@ -178579,9 +179143,14 @@ function registerListCommand(program) {
178579
179143
  const rows = [];
178580
179144
  for (const pkg of packages) {
178581
179145
  const res = await listExports({ entryFile: pkg.entry });
178582
- rows.push({ name: pkg.name, count: res.exports.length });
179146
+ let filtered = res.exports;
179147
+ if (options.undocumented) {
179148
+ filtered = filtered.filter((e) => !e.description || e.description.trim().length === 0);
179149
+ }
179150
+ rows.push({ name: pkg.name, count: filtered.length });
178583
179151
  }
178584
- formatOutput("list", { packages: rows }, startTime, version, renderBatchList);
179152
+ const filter2 = options.undocumented ? "undocumented" : undefined;
179153
+ formatOutput("list", { packages: rows, filter: filter2 }, startTime, version, renderBatchList);
178585
179154
  return;
178586
179155
  }
178587
179156
  let entryFile;
@@ -178618,6 +179187,7 @@ function registerListCommand(program) {
178618
179187
  const matches = fuzzySearch(searchTerm, exports);
178619
179188
  exports = matches.map((m) => exports.find((e) => e.name === m.name));
178620
179189
  }
179190
+ const filter = options.undocumented ? "undocumented" : options.drifted ? "drifted" : undefined;
178621
179191
  const data = {
178622
179192
  exports: exports.map((e) => ({
178623
179193
  name: e.name,
@@ -178626,7 +179196,8 @@ function registerListCommand(program) {
178626
179196
  ...e.deprecated ? { deprecated: true } : {}
178627
179197
  })),
178628
179198
  ...searchTerm ? { search: searchTerm } : {},
178629
- showAll: !!options.full
179199
+ showAll: !!options.full,
179200
+ ...filter ? { filter } : {}
178630
179201
  };
178631
179202
  formatOutput("list", data, startTime, version, renderList);
178632
179203
  } catch (err) {
@@ -178968,7 +179539,7 @@ function registerReportCommand(program) {
178968
179539
  import { readFileSync as readFileSync36 } from "node:fs";
178969
179540
  import * as path40 from "node:path";
178970
179541
  import { fileURLToPath as fileURLToPath20 } from "node:url";
178971
- import { diffSpec as diffSpec7, recommendSemverBump as recommendSemverBump3 } from "@openpkg-ts/spec";
179542
+ import { diffSpec as diffSpec8, recommendSemverBump as recommendSemverBump3 } from "@openpkg-ts/spec";
178972
179543
 
178973
179544
  // src/formatters/semver.ts
178974
179545
  function renderSemver(data) {
@@ -178997,7 +179568,7 @@ function registerSemverCommand(program) {
178997
179568
  try {
178998
179569
  const args = [oldPath, newPath].filter(Boolean);
178999
179570
  const { oldSpec, newSpec } = await resolveSpecs({ args, ...options });
179000
- const diff = diffSpec7(oldSpec, newSpec);
179571
+ const diff = diffSpec8(oldSpec, newSpec);
179001
179572
  const recommendation = recommendSemverBump3(diff);
179002
179573
  const data = {
179003
179574
  bump: recommendation.bump,
@@ -179158,6 +179729,12 @@ if (process.argv.includes("--capabilities")) {
179158
179729
  `);
179159
179730
  process.exit(0);
179160
179731
  }
179732
+ var userArgs = process.argv.slice(2).filter((a) => !a.startsWith("-"));
179733
+ if (userArgs.length === 0) {
179734
+ const { configPath } = loadConfig();
179735
+ const subcommand = configPath ? "health" : "init";
179736
+ process.argv.splice(2, 0, subcommand);
179737
+ }
179161
179738
  program.parseAsync().catch(() => {
179162
179739
  process.exit(1);
179163
179740
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@driftdev/cli",
3
- "version": "0.36.0",
3
+ "version": "0.38.0",
4
4
  "description": "Drift CLI - Documentation coverage and drift detection for TypeScript",
5
5
  "keywords": [
6
6
  "typescript",