@driftdev/cli 1.12.0 → 1.12.2

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 +88 -52
  2. package/package.json +2 -2
package/dist/drift.js CHANGED
@@ -52,7 +52,7 @@ var init_global = () => {};
52
52
 
53
53
  // src/drift.ts
54
54
  import { readFileSync as readFileSync23 } from "node:fs";
55
- import * as path34 from "node:path";
55
+ import * as path35 from "node:path";
56
56
  import { fileURLToPath as fileURLToPath2 } from "node:url";
57
57
  import { Command } from "commander";
58
58
 
@@ -80,12 +80,17 @@ var cached;
80
80
  function getVersion() {
81
81
  if (cached)
82
82
  return cached;
83
- try {
84
- cached = JSON.parse(readFileSync(join2(__dirname2, "../../package.json"), "utf-8")).version ?? "0.0.0";
85
- } catch {
86
- cached = "0.0.0";
83
+ for (const rel of ["../package.json", "../../package.json"]) {
84
+ try {
85
+ const pkg = JSON.parse(readFileSync(join2(__dirname2, rel), "utf-8"));
86
+ if (pkg.name === "@driftdev/cli" && typeof pkg.version === "string") {
87
+ cached = pkg.version;
88
+ return pkg.version;
89
+ }
90
+ } catch {}
87
91
  }
88
- return cached ?? "0.0.0";
92
+ cached = "0.0.0";
93
+ return cached;
89
94
  }
90
95
 
91
96
  // src/cache/spec-cache.ts
@@ -4266,7 +4271,7 @@ function registerInitCommand(program) {
4266
4271
  }
4267
4272
 
4268
4273
  // src/commands/lint.ts
4269
- import * as path28 from "node:path";
4274
+ import * as path29 from "node:path";
4270
4275
  import { buildExportRegistry, computeDrift as computeDrift4, detectProseDrift } from "@driftdev/sdk";
4271
4276
 
4272
4277
  // src/formatters/lint.ts
@@ -4304,6 +4309,13 @@ function renderLint(data, next) {
4304
4309
  }
4305
4310
 
4306
4311
  // src/utils/annotations.ts
4312
+ import * as path28 from "node:path";
4313
+ function annotationPath(filePath) {
4314
+ if (!path28.isAbsolute(filePath))
4315
+ return filePath;
4316
+ const rel = path28.relative(process.cwd(), filePath);
4317
+ return rel.startsWith("..") ? filePath : rel;
4318
+ }
4307
4319
  function escapeData(value) {
4308
4320
  return value.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
4309
4321
  }
@@ -4314,7 +4326,7 @@ function emitAnnotations(issues, level = "error") {
4314
4326
  for (const issue of issues) {
4315
4327
  const props = [];
4316
4328
  if (issue.filePath)
4317
- props.push(`file=${escapeProperty(issue.filePath)}`);
4329
+ props.push(`file=${escapeProperty(annotationPath(issue.filePath))}`);
4318
4330
  if (issue.line !== undefined)
4319
4331
  props.push(`line=${issue.line}`);
4320
4332
  props.push(`title=${escapeProperty(`drift: ${issue.export || "docs"}`)}`);
@@ -4377,9 +4389,9 @@ function registerLintCommand(program) {
4377
4389
  formatOutput("lint", { issues: [], count: 0 }, startTime, version, renderLint);
4378
4390
  return;
4379
4391
  }
4380
- let entryFile = entry ? path28.resolve(process.cwd(), entry) : undefined;
4392
+ let entryFile = entry ? path29.resolve(process.cwd(), entry) : undefined;
4381
4393
  if (lang === "typescript" && !entryFile) {
4382
- entryFile = config.entry ? path28.resolve(process.cwd(), config.entry) : detectEntry();
4394
+ entryFile = config.entry ? path29.resolve(process.cwd(), config.entry) : detectEntry();
4383
4395
  }
4384
4396
  const { apiSpec: spec } = await resolveTruth({
4385
4397
  entry: entryFile,
@@ -4445,7 +4457,7 @@ function registerLintCommand(program) {
4445
4457
  }
4446
4458
 
4447
4459
  // src/commands/list.ts
4448
- import * as path29 from "node:path";
4460
+ import * as path30 from "node:path";
4449
4461
  import { computeDrift as computeDrift5 } from "@driftdev/sdk";
4450
4462
  import { listExports as listExports2 } from "@openpkg-ts/sdk";
4451
4463
 
@@ -4560,7 +4572,7 @@ function registerListCommand(program) {
4560
4572
  } else {
4561
4573
  let entryFile;
4562
4574
  if (searchOrEntry && looksLikeFilePath(searchOrEntry)) {
4563
- entryFile = path29.resolve(process.cwd(), searchOrEntry);
4575
+ entryFile = path30.resolve(process.cwd(), searchOrEntry);
4564
4576
  } else if (searchOrEntry) {
4565
4577
  entryFile = detectEntry();
4566
4578
  searchTerm = searchOrEntry;
@@ -4817,7 +4829,7 @@ function diffArgs(command, args) {
4817
4829
  // src/commands/release.ts
4818
4830
  import { execSync as execSync4 } from "node:child_process";
4819
4831
  import { existsSync as existsSync15, readFileSync as readFileSync20 } from "node:fs";
4820
- import * as path30 from "node:path";
4832
+ import * as path31 from "node:path";
4821
4833
  import { computeDrift as computeDrift6 } from "@driftdev/sdk";
4822
4834
 
4823
4835
  // src/formatters/release.ts
@@ -4865,7 +4877,7 @@ function getLastTag() {
4865
4877
  }
4866
4878
  }
4867
4879
  function getPackageVersion(cwd) {
4868
- const pkgPath = path30.join(cwd, "package.json");
4880
+ const pkgPath = path31.join(cwd, "package.json");
4869
4881
  if (!existsSync15(pkgPath))
4870
4882
  return null;
4871
4883
  try {
@@ -4881,7 +4893,7 @@ function registerReleaseCommand(program) {
4881
4893
  const cwd = process.cwd();
4882
4894
  try {
4883
4895
  const { config } = loadConfig();
4884
- const entryFile = entry ? path30.resolve(cwd, entry) : config.entry ? path30.resolve(cwd, config.entry) : detectEntry();
4896
+ const entryFile = entry ? path31.resolve(cwd, entry) : config.entry ? path31.resolve(cwd, config.entry) : detectEntry();
4885
4897
  const { spec } = await cachedExtract(entryFile);
4886
4898
  const exports = spec.exports ?? [];
4887
4899
  const total = exports.length;
@@ -5006,7 +5018,7 @@ function renderReport(data) {
5006
5018
  // src/utils/scan-packages.ts
5007
5019
  import { execSync as execSync5 } from "node:child_process";
5008
5020
  import { existsSync as existsSync16, readFileSync as readFileSync21 } from "node:fs";
5009
- import * as path31 from "node:path";
5021
+ import * as path32 from "node:path";
5010
5022
  import { computeDrift as computeDrift7 } from "@driftdev/sdk";
5011
5023
  function detectPackageDirs2(cwd) {
5012
5024
  const workspaces = detectWorkspaces(cwd);
@@ -5025,11 +5037,11 @@ async function scanAllPackages(cwd) {
5025
5037
  const packageDirs = detectPackageDirs2(cwd);
5026
5038
  const results = [];
5027
5039
  for (const dir of packageDirs) {
5028
- const absDir = dir === "." ? cwd : path31.join(cwd, dir);
5040
+ const absDir = dir === "." ? cwd : path32.join(cwd, dir);
5029
5041
  if (!existsSync16(absDir))
5030
5042
  continue;
5031
5043
  let name = dir;
5032
- const pkgPath = path31.join(absDir, "package.json");
5044
+ const pkgPath = path32.join(absDir, "package.json");
5033
5045
  if (existsSync16(pkgPath)) {
5034
5046
  try {
5035
5047
  const pkg = JSON.parse(readFileSync21(pkgPath, "utf-8"));
@@ -5128,7 +5140,7 @@ function registerReportCommand(program) {
5128
5140
  }
5129
5141
 
5130
5142
  // src/commands/scan.ts
5131
- import * as path32 from "node:path";
5143
+ import * as path33 from "node:path";
5132
5144
  import {
5133
5145
  buildExportRegistry as buildExportRegistry2,
5134
5146
  computeDrift as computeDrift8,
@@ -5144,13 +5156,15 @@ function renderScan(data, next) {
5144
5156
  lines.push(indent(`${c.bold(data.packageName)}${c.gray(ver)}`));
5145
5157
  lines.push("");
5146
5158
  }
5147
- const hColor = coverageColor(data.health);
5148
- lines.push(indent(`Health ${hColor(`${data.health}%`)}`));
5149
- const externalNote = data.coverage.external ? c.gray(` +${data.coverage.external} external (not resolvable here)`) : "";
5150
- lines.push(indent(`Coverage ${coverageColor(data.coverage.score)(`${data.coverage.score}%`)} (${data.coverage.documented}/${data.coverage.total} exports)${externalNote}`));
5151
- lines.push(indent(`Lint ${data.lint.count === 0 ? c.green("0 issues") : c.red(`${data.lint.count} issues`)}`));
5152
- lines.push("");
5153
- if (data.lint.count > 0) {
5159
+ if (data.health !== undefined && data.coverage && data.lint) {
5160
+ const hColor = coverageColor(data.health);
5161
+ lines.push(indent(`Health ${hColor(`${data.health}%`)}`));
5162
+ const externalNote = data.coverage.external ? c.gray(` +${data.coverage.external} external (not resolvable here)`) : "";
5163
+ lines.push(indent(`Coverage ${coverageColor(data.coverage.score)(`${data.coverage.score}%`)} (${data.coverage.documented}/${data.coverage.total} exports)${externalNote}`));
5164
+ lines.push(indent(`Lint ${data.lint.count === 0 ? c.green("0 issues") : c.red(`${data.lint.count} issues`)}`));
5165
+ lines.push("");
5166
+ }
5167
+ if (data.lint && data.lint.count > 0) {
5154
5168
  lines.push(indent("Issues"));
5155
5169
  lines.push(indent(c.gray(separator())));
5156
5170
  const shown = data.lint.issues.slice(0, 10);
@@ -5210,6 +5224,25 @@ function renderBatchScan(data) {
5210
5224
  }
5211
5225
 
5212
5226
  // src/commands/scan.ts
5227
+ function toDocsCoverageResult(run) {
5228
+ return {
5229
+ pass: run.pass,
5230
+ pages: run.pages.map((p) => ({
5231
+ page: p.page,
5232
+ type: p.type,
5233
+ status: p.status,
5234
+ baselineGaps: p.baselineGaps,
5235
+ counts: p.result.counts,
5236
+ failures: p.failures,
5237
+ warnings: p.warnings,
5238
+ gaps: p.result.gaps,
5239
+ ghosts: p.result.ghosts,
5240
+ inversions: p.result.inversions,
5241
+ documentedKeysFromOtherTypes: p.result.documentedKeysFromOtherTypes,
5242
+ annotated: p.result.annotated
5243
+ }))
5244
+ };
5245
+ }
5213
5246
  function registerScanCommand(program) {
5214
5247
  program.command("scan [entry]").description("Run coverage + lint + prose drift in one pass").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").option("--lang <language>", "Source language (inferred from --spec/--abi/.clar; default typescript)").option("--abi <path>", "ABI JSON file (required for --lang clarity)").option("--spec <path>", "OpenAPI document: path or URL (implies --lang openapi)").option("--docs <patterns...>", "Markdown corpus for prose drift: glob patterns or directories (overrides repo-local defaults)").option("--docs-map <file>", "Docs map (page→type) activating key-coverage mode: gaps/ghosts/inversions per page").option("--annotations", "Emit GitHub Actions ::error/::warning annotations for findings").action(async (entry, options) => {
5215
5248
  const startTime = Date.now();
@@ -5289,10 +5322,31 @@ function registerScanCommand(program) {
5289
5322
  process.exitCode = 1;
5290
5323
  return;
5291
5324
  }
5325
+ if (options.docsMap && !entry) {
5326
+ const loaded = loadDocsMap(options.docsMap);
5327
+ if (loaded.map.pages.every((p) => p.spec || p.entry)) {
5328
+ const run = await runDocsCoverage(loaded);
5329
+ const data2 = { pass: run.pass, docsCoverage: toDocsCoverageResult(run) };
5330
+ formatOutput("scan", data2, startTime, version, renderScan);
5331
+ if (options.annotations) {
5332
+ emitAnnotations(run.annotations.errors, "error");
5333
+ emitAnnotations(run.annotations.warnings, "warning");
5334
+ }
5335
+ if (!run.pass) {
5336
+ if (!shouldRenderHuman()) {
5337
+ const covFails = run.pages.flatMap((p) => p.failures.map((f) => `${p.page}: ${f}`));
5338
+ process.stderr.write(`scan failed: docs coverage: ${covFails.join(" | ")}
5339
+ `);
5340
+ }
5341
+ process.exitCode = 1;
5342
+ }
5343
+ return;
5344
+ }
5345
+ }
5292
5346
  const { config } = loadConfig();
5293
- let entryFile = entry ? path32.resolve(process.cwd(), entry) : undefined;
5347
+ let entryFile = entry ? path33.resolve(process.cwd(), entry) : undefined;
5294
5348
  if (lang === "typescript" && !entryFile) {
5295
- entryFile = config.entry ? path32.resolve(process.cwd(), config.entry) : detectEntry();
5349
+ entryFile = config.entry ? path33.resolve(process.cwd(), config.entry) : detectEntry();
5296
5350
  }
5297
5351
  const { apiSpec, packageName, packageVersion } = await resolveTruth({
5298
5352
  entry: entryFile,
@@ -5374,25 +5428,7 @@ function registerScanCommand(program) {
5374
5428
  pass,
5375
5429
  packageName,
5376
5430
  packageVersion,
5377
- ...docsCoverage ? {
5378
- docsCoverage: {
5379
- pass: docsCoverage.pass,
5380
- pages: docsCoverage.pages.map((p) => ({
5381
- page: p.page,
5382
- type: p.type,
5383
- status: p.status,
5384
- baselineGaps: p.baselineGaps,
5385
- counts: p.result.counts,
5386
- failures: p.failures,
5387
- warnings: p.warnings,
5388
- gaps: p.result.gaps,
5389
- ghosts: p.result.ghosts,
5390
- inversions: p.result.inversions,
5391
- documentedKeysFromOtherTypes: p.result.documentedKeysFromOtherTypes,
5392
- annotated: p.result.annotated
5393
- }))
5394
- }
5395
- } : {}
5431
+ ...docsCoverage ? { docsCoverage: toDocsCoverageResult(docsCoverage) } : {}
5396
5432
  };
5397
5433
  let next;
5398
5434
  if (issues.length > 0) {
@@ -5468,7 +5504,7 @@ function registerSemverCommand(program) {
5468
5504
 
5469
5505
  // src/commands/validate.ts
5470
5506
  import { readFileSync as readFileSync22 } from "node:fs";
5471
- import * as path33 from "node:path";
5507
+ import * as path34 from "node:path";
5472
5508
  import { validateSpec } from "@openpkg-ts/spec";
5473
5509
 
5474
5510
  // src/formatters/validate.ts
@@ -5494,7 +5530,7 @@ function registerValidateCommand(program) {
5494
5530
  const startTime = Date.now();
5495
5531
  const version = getVersion();
5496
5532
  try {
5497
- const filePath = path33.resolve(process.cwd(), file);
5533
+ const filePath = path34.resolve(process.cwd(), file);
5498
5534
  const content = readFileSync22(filePath, "utf-8");
5499
5535
  const spec = JSON.parse(content);
5500
5536
  const result = validateSpec(spec);
@@ -5637,13 +5673,13 @@ function extractCapabilities(program) {
5637
5673
 
5638
5674
  // src/drift.ts
5639
5675
  var __filename2 = fileURLToPath2(import.meta.url);
5640
- var __dirname3 = path34.dirname(__filename2);
5641
- var packageJson = JSON.parse(readFileSync23(path34.join(__dirname3, "../package.json"), "utf-8"));
5676
+ var __dirname3 = path35.dirname(__filename2);
5677
+ var packageJson = JSON.parse(readFileSync23(path35.join(__dirname3, "../package.json"), "utf-8"));
5642
5678
  var program = new Command;
5643
5679
  program.name("drift").description("drift — detect when your docs drift from your code").version(packageJson.version).option("--json", "Force JSON output (default when piped)").option("--human", "Force human-readable output (default in terminal)").option("--config <path>", "Path to drift config file").option("--cwd <dir>", "Run as if started in <dir>").option("--no-cache", "Bypass spec cache").option("--tools", "List all available tools for agent use (JSON)").hook("preAction", (_thisCommand) => {
5644
5680
  const opts = program.opts();
5645
5681
  if (opts.cwd) {
5646
- process.chdir(path34.resolve(opts.cwd));
5682
+ process.chdir(path35.resolve(opts.cwd));
5647
5683
  }
5648
5684
  setOutputMode({ json: opts.json, human: opts.human });
5649
5685
  setConfigPath(opts.config);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@driftdev/cli",
3
- "version": "1.12.0",
3
+ "version": "1.12.2",
4
4
  "description": "Drift CLI - detect when your docs drift from your code",
5
5
  "keywords": [
6
6
  "typescript",
@@ -47,7 +47,7 @@
47
47
  "dependencies": {
48
48
  "@driftdev/clarity-adapter": "^1.0.1",
49
49
  "@driftdev/openapi-adapter": "^1.0.1",
50
- "@driftdev/sdk": "^1.12.0",
50
+ "@driftdev/sdk": "^1.12.2",
51
51
  "@modelcontextprotocol/sdk": "^1.29.0",
52
52
  "@openpkg-ts/sdk": "^0.43.0",
53
53
  "@openpkg-ts/spec": "^0.43.0",