@driftdev/cli 0.40.0 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/drift.js +135 -93
  3. package/package.json +2 -3
package/README.md CHANGED
@@ -95,7 +95,7 @@ Entry auto-detects from `package.json` (`types`, `exports`, `main`, `module`, `b
95
95
 
96
96
  ```bash
97
97
  # Machine-readable list of all commands + flags
98
- drift --capabilities
98
+ drift --tools
99
99
  ```
100
100
 
101
101
  ## Global Options
package/dist/drift.js CHANGED
@@ -1134,6 +1134,26 @@ function registerBreakingCommand(program) {
1134
1134
  });
1135
1135
  }
1136
1136
 
1137
+ // src/commands/commands.ts
1138
+ var GROUPS = {
1139
+ Composed: ["scan", "ci", "health"],
1140
+ Analysis: ["coverage", "lint", "examples"],
1141
+ Extraction: ["extract", "list", "get"],
1142
+ Comparison: ["diff", "breaking", "semver", "changelog"],
1143
+ Setup: ["init", "config", "context"],
1144
+ Plumbing: ["validate", "filter", "cache", "report", "release"]
1145
+ };
1146
+ function registerCommandsCommand(program) {
1147
+ program.command("commands").description("List all available commands grouped by category").action(() => {
1148
+ const maxGroup = Math.max(...Object.keys(GROUPS).map((g) => g.length));
1149
+ for (const [group, cmds] of Object.entries(GROUPS)) {
1150
+ const pad2 = " ".repeat(maxGroup - group.length);
1151
+ process.stdout.write(` ${group}${pad2} ${cmds.join(", ")}
1152
+ `);
1153
+ }
1154
+ });
1155
+ }
1156
+
1137
1157
  // src/formatters/cache.ts
1138
1158
  function renderCacheStatus(data) {
1139
1159
  const lines = [];
@@ -1388,6 +1408,30 @@ function renderContextMarkdown(data) {
1388
1408
  lines.push(`**Average coverage**: ${avgCoverage}% `);
1389
1409
  lines.push(`**Total lint issues**: ${totalIssues}`);
1390
1410
  lines.push("");
1411
+ for (const pkg of data.packages) {
1412
+ if (pkg.issues && pkg.issues.length > 0) {
1413
+ lines.push(`### ${pkg.name} — Issues`);
1414
+ lines.push("");
1415
+ lines.push("| Export | Type | Location |");
1416
+ lines.push("|--------|------|----------|");
1417
+ for (const issue of pkg.issues) {
1418
+ const loc = issue.filePath ? `${issue.filePath}${issue.line ? `:${issue.line}` : ""}` : "—";
1419
+ lines.push(`| ${issue.export} | ${issue.type} | ${loc} |`);
1420
+ }
1421
+ lines.push("");
1422
+ }
1423
+ if (pkg.undocumentedExports && pkg.undocumentedExports.length > 0) {
1424
+ lines.push(`### ${pkg.name} — Undocumented Exports`);
1425
+ lines.push("");
1426
+ lines.push("| Export | Kind | Location |");
1427
+ lines.push("|--------|------|----------|");
1428
+ for (const exp of pkg.undocumentedExports) {
1429
+ const loc = exp.filePath ? `${exp.filePath}${exp.line ? `:${exp.line}` : ""}` : "—";
1430
+ lines.push(`| ${exp.name} | ${exp.kind} | ${loc} |`);
1431
+ }
1432
+ lines.push("");
1433
+ }
1434
+ }
1391
1435
  }
1392
1436
  if (data.history.length > 0) {
1393
1437
  lines.push("## Recent Activity");
@@ -2061,6 +2105,52 @@ function getCommitSha2() {
2061
2105
  return null;
2062
2106
  }
2063
2107
  }
2108
+ function buildPackageContext(name, spec) {
2109
+ const exports = spec.exports ?? [];
2110
+ let documented = 0;
2111
+ const undocumented = [];
2112
+ const undocumentedExports = [];
2113
+ for (const exp of exports) {
2114
+ if (exp.description?.trim()) {
2115
+ documented++;
2116
+ } else {
2117
+ undocumented.push(exp.name);
2118
+ undocumentedExports.push({
2119
+ name: exp.name,
2120
+ kind: exp.kind,
2121
+ filePath: exp.source?.file,
2122
+ line: exp.source?.line
2123
+ });
2124
+ }
2125
+ }
2126
+ const coverage = exports.length > 0 ? Math.round(documented / exports.length * 100) : 100;
2127
+ const driftResult = computeDrift2(spec);
2128
+ const issues = [];
2129
+ let lintIssues = 0;
2130
+ for (const [exportName, drifts] of driftResult.exports) {
2131
+ lintIssues += drifts.length;
2132
+ const exp = exports.find((e) => e.name === exportName);
2133
+ for (const drift of drifts) {
2134
+ issues.push({
2135
+ export: exportName,
2136
+ type: drift.type,
2137
+ issue: drift.issue,
2138
+ filePath: drift.filePath ?? exp?.source?.file,
2139
+ line: drift.line ?? exp?.source?.line
2140
+ });
2141
+ }
2142
+ }
2143
+ return {
2144
+ name,
2145
+ coverage,
2146
+ lintIssues,
2147
+ exports: exports.length,
2148
+ documented,
2149
+ undocumented,
2150
+ issues: issues.length > 0 ? issues : undefined,
2151
+ undocumentedExports: undocumentedExports.length > 0 ? undocumentedExports : undefined
2152
+ };
2153
+ }
2064
2154
  function registerContextCommand(program) {
2065
2155
  program.command("context [entry]").description("Generate agent context file with project state").option("--all", "Include all workspace packages").option("--private", "Include private packages in --all mode").option("--output <path>", "Output path (default: ~/.drift/projects/<slug>/context.md)").action(async (entry, options) => {
2066
2156
  const startTime = Date.now();
@@ -2078,28 +2168,7 @@ function registerContextCommand(program) {
2078
2168
  for (const pkg of pkgs) {
2079
2169
  try {
2080
2170
  const { spec } = await cachedExtract(pkg.entry);
2081
- const exports = spec.exports ?? [];
2082
- let documented = 0;
2083
- const undocumented = [];
2084
- for (const exp of exports) {
2085
- if (exp.description?.trim())
2086
- documented++;
2087
- else
2088
- undocumented.push(exp.name);
2089
- }
2090
- const coverage = exports.length > 0 ? Math.round(documented / exports.length * 100) : 100;
2091
- const driftResult = computeDrift2(spec);
2092
- let lintIssues = 0;
2093
- for (const [, drifts] of driftResult.exports)
2094
- lintIssues += drifts.length;
2095
- packages.push({
2096
- name: pkg.name,
2097
- coverage,
2098
- lintIssues,
2099
- exports: exports.length,
2100
- documented,
2101
- undocumented
2102
- });
2171
+ packages.push(buildPackageContext(pkg.name, spec));
2103
2172
  } catch {
2104
2173
  packages.push({
2105
2174
  name: pkg.name,
@@ -2114,64 +2183,22 @@ function registerContextCommand(program) {
2114
2183
  } else {
2115
2184
  const entryFile = config.entry ? path14.resolve(cwd, config.entry) : detectEntry();
2116
2185
  const { spec } = await cachedExtract(entryFile);
2117
- const exports = spec.exports ?? [];
2118
- let documented = 0;
2119
- const undocumented = [];
2120
- for (const exp of exports) {
2121
- if (exp.description?.trim())
2122
- documented++;
2123
- else
2124
- undocumented.push(exp.name);
2125
- }
2126
- const coverage = exports.length > 0 ? Math.round(documented / exports.length * 100) : 100;
2127
- const driftResult = computeDrift2(spec);
2128
- let lintIssues = 0;
2129
- for (const [, drifts] of driftResult.exports)
2130
- lintIssues += drifts.length;
2131
2186
  const pkgJsonPath = path14.resolve(cwd, "package.json");
2132
2187
  let name = path14.basename(cwd);
2133
2188
  try {
2134
2189
  name = JSON.parse(readFileSync12(pkgJsonPath, "utf-8")).name ?? name;
2135
2190
  } catch {}
2136
- packages.push({
2137
- name,
2138
- coverage,
2139
- lintIssues,
2140
- exports: exports.length,
2141
- documented,
2142
- undocumented
2143
- });
2191
+ packages.push(buildPackageContext(name, spec));
2144
2192
  }
2145
2193
  } else {
2146
2194
  const entryFile = path14.resolve(cwd, entry);
2147
2195
  const { spec } = await cachedExtract(entryFile);
2148
- const exports = spec.exports ?? [];
2149
- let documented = 0;
2150
- const undocumented = [];
2151
- for (const exp of exports) {
2152
- if (exp.description?.trim())
2153
- documented++;
2154
- else
2155
- undocumented.push(exp.name);
2156
- }
2157
- const coverage = exports.length > 0 ? Math.round(documented / exports.length * 100) : 100;
2158
- const driftResult = computeDrift2(spec);
2159
- let lintIssues = 0;
2160
- for (const [, drifts] of driftResult.exports)
2161
- lintIssues += drifts.length;
2162
2196
  const pkgJsonPath = path14.resolve(cwd, "package.json");
2163
2197
  let name = path14.basename(cwd);
2164
2198
  try {
2165
2199
  name = JSON.parse(readFileSync12(pkgJsonPath, "utf-8")).name ?? name;
2166
2200
  } catch {}
2167
- packages.push({
2168
- name,
2169
- coverage,
2170
- lintIssues,
2171
- exports: exports.length,
2172
- documented,
2173
- undocumented
2174
- });
2201
+ packages.push(buildPackageContext(name, spec));
2175
2202
  }
2176
2203
  const contextData = { packages, history, config, commit: commit ?? null };
2177
2204
  if (options.output) {
@@ -3601,8 +3628,7 @@ import {
3601
3628
  buildExportRegistry,
3602
3629
  computeDrift as computeDrift4,
3603
3630
  detectProseDrift,
3604
- discoverMarkdownFiles,
3605
- isFixableDrift
3631
+ discoverMarkdownFiles
3606
3632
  } from "@driftdev/sdk";
3607
3633
 
3608
3634
  // src/formatters/lint.ts
@@ -3715,16 +3741,9 @@ function registerLintCommand(program) {
3715
3741
  }
3716
3742
  } catch {}
3717
3743
  const data = { issues, count: issues.length };
3718
- let fixableCount = 0;
3719
- for (const [, drifts] of driftResult.exports) {
3720
- for (const d of drifts) {
3721
- if (isFixableDrift(d))
3722
- fixableCount++;
3723
- }
3724
- }
3725
3744
  const next = issues.length > 0 ? {
3726
3745
  suggested: "drift-fix skill",
3727
- reason: `${fixableCount} of ${issues.length} issues are auto-fixable`
3746
+ reason: `${issues.length} issue${issues.length === 1 ? "" : "s"} found`
3728
3747
  } : undefined;
3729
3748
  formatOutput("lint", data, startTime, version, renderLint, next);
3730
3749
  if (issues.length > 0) {
@@ -4197,8 +4216,7 @@ import {
4197
4216
  buildExportRegistry as buildExportRegistry2,
4198
4217
  computeDrift as computeDrift8,
4199
4218
  detectProseDrift as detectProseDrift2,
4200
- discoverMarkdownFiles as discoverMarkdownFiles2,
4201
- isFixableDrift as isFixableDrift2
4219
+ discoverMarkdownFiles as discoverMarkdownFiles2
4202
4220
  } from "@driftdev/sdk";
4203
4221
 
4204
4222
  // src/formatters/scan.ts
@@ -4387,16 +4405,9 @@ function registerScanCommand(program) {
4387
4405
  };
4388
4406
  let next;
4389
4407
  if (issues.length > 0) {
4390
- let fixableCount = 0;
4391
- for (const [, drs] of driftResult.exports) {
4392
- for (const d of drs) {
4393
- if (isFixableDrift2(d))
4394
- fixableCount++;
4395
- }
4396
- }
4397
4408
  next = {
4398
4409
  suggested: "drift-fix skill",
4399
- reason: `${fixableCount} of ${issues.length} issues are auto-fixable`
4410
+ reason: `${issues.length} issue${issues.length === 1 ? "" : "s"} found`
4400
4411
  };
4401
4412
  } else if (total - documented > 0) {
4402
4413
  next = {
@@ -4516,6 +4527,29 @@ function extractFlags(cmd) {
4516
4527
  type: optionType(opt)
4517
4528
  }));
4518
4529
  }
4530
+ var COMMAND_EXAMPLES = {
4531
+ scan: ["drift scan --json", "drift scan --all --json", "drift scan --ci --json"],
4532
+ lint: ["drift lint --json", "drift lint --all --json"],
4533
+ coverage: ["drift coverage --json", "drift coverage --min 80 --json"],
4534
+ extract: ["drift extract --json"],
4535
+ list: ["drift list --json"],
4536
+ get: ["drift get createClient --json"],
4537
+ diff: ["drift diff --base main --json"],
4538
+ breaking: ["drift breaking --base main --json"],
4539
+ semver: ["drift semver --base main --json"],
4540
+ changelog: ["drift changelog --base main --json"],
4541
+ ci: ["drift ci --json", "drift ci --all --json"],
4542
+ release: ["drift release --json"],
4543
+ context: ["drift context --json", "drift context --all --json"],
4544
+ examples: ["drift examples --typecheck --json"],
4545
+ health: ["drift health --json"],
4546
+ config: ["drift config list --json", "drift config get coverage.min --json"],
4547
+ init: ["drift init --json"],
4548
+ validate: ["drift validate spec.json --json"],
4549
+ filter: ["drift filter spec.json --kind function --json"],
4550
+ report: ["drift report --json"],
4551
+ cache: ["drift cache status", "drift cache clear"]
4552
+ };
4519
4553
  function extractCapabilities(program) {
4520
4554
  const commands = [];
4521
4555
  for (const cmd of program.commands) {
@@ -4524,11 +4558,14 @@ function extractCapabilities(program) {
4524
4558
  name: cmd.name(),
4525
4559
  description: cmd.description(),
4526
4560
  flags: extractFlags(cmd),
4527
- ...positionalArgs.length > 0 ? { positional: positionalArgs.map((a) => a.name()).join(" ") } : {}
4561
+ ...positionalArgs.length > 0 ? { positional: positionalArgs.map((a) => a.name()).join(" ") } : {},
4562
+ ...COMMAND_EXAMPLES[cmd.name()] ? { examples: COMMAND_EXAMPLES[cmd.name()] } : {}
4528
4563
  });
4529
4564
  }
4530
4565
  return {
4531
4566
  version: program.version() ?? "0.0.0",
4567
+ hint: "Run 'drift' for human output. Use these primitives with --json for agent workflows.",
4568
+ humanCommands: ["scan", "ci", "init"],
4532
4569
  commands,
4533
4570
  globalFlags: extractFlags(program),
4534
4571
  entities: [
@@ -4579,7 +4616,7 @@ var __filename2 = fileURLToPath2(import.meta.url);
4579
4616
  var __dirname3 = path29.dirname(__filename2);
4580
4617
  var packageJson = JSON.parse(readFileSync22(path29.join(__dirname3, "../package.json"), "utf-8"));
4581
4618
  var program = new Command;
4582
- program.name("drift").description("drift — documentation quality primitives for TypeScript").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").hook("preAction", (_thisCommand) => {
4619
+ program.name("drift").description("drift — documentation quality for TypeScript").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) => {
4583
4620
  const opts = program.opts();
4584
4621
  if (opts.cwd) {
4585
4622
  process.chdir(path29.resolve(opts.cwd));
@@ -4610,19 +4647,24 @@ registerInitCommand(program);
4610
4647
  registerConfigCommand(program);
4611
4648
  registerContextCommand(program);
4612
4649
  registerCacheCommand(program);
4613
- if (process.argv.includes("--capabilities")) {
4650
+ registerCommandsCommand(program);
4651
+ var HUMAN_COMMANDS = new Set(["scan", "ci", "init", "commands"]);
4652
+ for (const cmd of program.commands) {
4653
+ if (!HUMAN_COMMANDS.has(cmd.name())) {
4654
+ cmd._hidden = true;
4655
+ }
4656
+ }
4657
+ if (process.argv.includes("--tools")) {
4614
4658
  const caps = extractCapabilities(program);
4615
4659
  process.stdout.write(`${JSON.stringify(caps, null, 2)}
4616
4660
  `);
4617
4661
  process.exit(0);
4618
4662
  }
4619
4663
  var rawArgs = process.argv.slice(2);
4620
- var hasHelpOrVersion = rawArgs.some((a) => ["-h", "--help", "-V", "--version"].includes(a));
4664
+ var hasHelpOrVersion = rawArgs.some((a) => ["-h", "--help", "-V", "--version", "--tools"].includes(a));
4621
4665
  var userArgs = rawArgs.filter((a) => !a.startsWith("-"));
4622
4666
  if (userArgs.length === 0 && !hasHelpOrVersion) {
4623
- const { configPath } = loadConfig();
4624
- const subcommand = configPath ? "health" : "init";
4625
- process.argv.splice(2, 0, subcommand);
4667
+ process.argv.splice(2, 0, "scan");
4626
4668
  }
4627
4669
  program.parseAsync().catch(() => {
4628
4670
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@driftdev/cli",
3
- "version": "0.40.0",
3
+ "version": "0.42.0",
4
4
  "description": "Drift CLI - Documentation coverage and drift detection for TypeScript",
5
5
  "keywords": [
6
6
  "typescript",
@@ -43,8 +43,7 @@
43
43
  "dist"
44
44
  ],
45
45
  "dependencies": {
46
- "@driftdev/sdk": "^0.39.0",
47
- "@driftdev/spec": "^0.36.0",
46
+ "@driftdev/sdk": "^0.42.0",
48
47
  "@openpkg-ts/sdk": "^0.37.0",
49
48
  "@openpkg-ts/spec": "^0.37.0",
50
49
  "chalk": "^5.4.1",