@doccov/cli 0.12.0 → 0.13.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 CHANGED
@@ -1,22 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from "node:module";
3
- var __create = Object.create;
4
- var __getProtoOf = Object.getPrototypeOf;
5
- var __defProp = Object.defineProperty;
6
- var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __toESM = (mod, isNodeMode, target) => {
9
- target = mod != null ? __create(__getProtoOf(mod)) : {};
10
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
- for (let key of __getOwnPropNames(mod))
12
- if (!__hasOwnProp.call(to, key))
13
- __defProp(to, key, {
14
- get: () => mod[key],
15
- enumerable: true
16
- });
17
- return to;
18
- };
19
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
2
 
21
3
  // src/config/doccov-config.ts
22
4
  import { access } from "node:fs/promises";
@@ -178,8 +160,8 @@ ${formatIssues(issues)}`);
178
160
  // src/config/index.ts
179
161
  var defineConfig = (config) => config;
180
162
  // src/cli.ts
181
- import { readFileSync as readFileSync4 } from "node:fs";
182
- import * as path10 from "node:path";
163
+ import { readFileSync as readFileSync3 } from "node:fs";
164
+ import * as path8 from "node:path";
183
165
  import { fileURLToPath } from "node:url";
184
166
  import { Command } from "commander";
185
167
 
@@ -1775,513 +1757,21 @@ var buildTemplate = (format) => {
1775
1757
  `);
1776
1758
  };
1777
1759
 
1778
- // src/commands/scan.ts
1779
- import * as fs6 from "node:fs";
1780
- import * as fsPromises from "node:fs/promises";
1781
- import * as os from "node:os";
1782
- import * as path8 from "node:path";
1783
- import {
1784
- buildCloneUrl,
1785
- buildDisplayUrl,
1786
- DocCov as DocCov3,
1787
- detectBuildInfo,
1788
- detectEntryPoint,
1789
- detectMonorepo,
1790
- detectPackageManager,
1791
- extractSpecSummary,
1792
- findPackageByName,
1793
- formatPackageList,
1794
- getInstallCommand,
1795
- NodeFileSystem as NodeFileSystem3,
1796
- parseGitHubUrl
1797
- } from "@doccov/sdk";
1798
- import {
1799
- DRIFT_CATEGORIES as DRIFT_CATEGORIES3,
1800
- DRIFT_CATEGORY_LABELS as DRIFT_CATEGORY_LABELS2
1801
- } from "@openpkg-ts/spec";
1802
- import chalk6 from "chalk";
1803
- import { simpleGit } from "simple-git";
1804
-
1805
- // src/utils/llm-build-plan.ts
1760
+ // src/commands/spec.ts
1806
1761
  import * as fs5 from "node:fs";
1807
1762
  import * as path7 from "node:path";
1808
- import { createAnthropic as createAnthropic3 } from "@ai-sdk/anthropic";
1809
- import { createOpenAI as createOpenAI3 } from "@ai-sdk/openai";
1810
- import { generateObject as generateObject3 } from "ai";
1811
- import { z as z4 } from "zod";
1812
- var BuildPlanSchema = z4.object({
1813
- installCommand: z4.string().optional().describe("Additional install command if needed"),
1814
- buildCommands: z4.array(z4.string()).describe('Build steps to run, e.g. ["npm run build:wasm"]'),
1815
- entryPoint: z4.string().describe("Path to TS/TSX entry file after build"),
1816
- notes: z4.string().optional().describe("Caveats or warnings")
1817
- });
1818
- var CONTEXT_FILES = [
1819
- "package.json",
1820
- "README.md",
1821
- "README",
1822
- "tsconfig.json",
1823
- "Cargo.toml",
1824
- ".nvmrc",
1825
- ".node-version",
1826
- "pnpm-workspace.yaml",
1827
- "lerna.json",
1828
- "wasm-pack.json"
1829
- ];
1830
- var MAX_FILE_CHARS = 2000;
1831
- function getModel3() {
1832
- const provider = process.env.DOCCOV_LLM_PROVIDER?.toLowerCase();
1833
- if (provider === "anthropic" || process.env.ANTHROPIC_API_KEY) {
1834
- const anthropic = createAnthropic3();
1835
- return anthropic("claude-sonnet-4-20250514");
1836
- }
1837
- const openai = createOpenAI3();
1838
- return openai("gpt-4o-mini");
1839
- }
1840
- async function gatherContextFiles(repoDir) {
1841
- const sections = [];
1842
- for (const fileName of CONTEXT_FILES) {
1843
- const filePath = path7.join(repoDir, fileName);
1844
- if (fs5.existsSync(filePath)) {
1845
- try {
1846
- let content = fs5.readFileSync(filePath, "utf-8");
1847
- if (content.length > MAX_FILE_CHARS) {
1848
- content = `${content.slice(0, MAX_FILE_CHARS)}
1849
- ... (truncated)`;
1850
- }
1851
- sections.push(`--- ${fileName} ---
1852
- ${content}`);
1853
- } catch {}
1854
- }
1855
- }
1856
- return sections.join(`
1857
-
1858
- `);
1859
- }
1860
- var BUILD_PLAN_PROMPT = (context) => `Analyze this project to determine how to build it for TypeScript API analysis.
1861
-
1862
- The standard entry detection failed. This might be a WASM project, unusual monorepo, or require a build step before the TypeScript entry point exists.
1863
-
1864
- <files>
1865
- ${context}
1866
- </files>
1867
-
1868
- Return:
1869
- - buildCommands: Commands to run in order (e.g., ["npm run build:wasm", "npm run build"]). Empty array if no build needed.
1870
- - entryPoint: Path to the TypeScript entry file AFTER build completes (e.g., "src/index.ts" or "pkg/index.d.ts")
1871
- - installCommand: Additional install command if needed beyond what was already run
1872
- - notes: Any caveats (e.g., "requires Rust/wasm-pack installed")
1873
-
1874
- Important:
1875
- - Look for build scripts in package.json that might generate TypeScript bindings
1876
- - Check README for build instructions
1877
- - For WASM projects, look for wasm-pack or similar tooling
1878
- - The entry point should be a .ts, .tsx, or .d.ts file`;
1879
- async function generateBuildPlan(repoDir) {
1880
- const hasApiKey = process.env.OPENAI_API_KEY || process.env.ANTHROPIC_API_KEY;
1881
- if (!hasApiKey) {
1882
- return null;
1883
- }
1884
- const context = await gatherContextFiles(repoDir);
1885
- if (!context.trim()) {
1886
- return null;
1887
- }
1888
- const model = getModel3();
1889
- const { object } = await generateObject3({
1890
- model,
1891
- schema: BuildPlanSchema,
1892
- prompt: BUILD_PLAN_PROMPT(context)
1893
- });
1894
- return object;
1895
- }
1896
-
1897
- // src/commands/scan.ts
1898
- var defaultDependencies4 = {
1899
- createDocCov: (options) => new DocCov3(options),
1900
- log: console.log,
1901
- error: console.error
1902
- };
1903
- function registerScanCommand(program, dependencies = {}) {
1904
- const { createDocCov, log, error } = {
1905
- ...defaultDependencies4,
1906
- ...dependencies
1907
- };
1908
- program.command("scan <url>").description("Analyze docs coverage for any public GitHub repository").option("--ref <branch>", "Branch or tag to analyze").option("--package <name>", "Target package in monorepo").option("--output <format>", "Output format: text or json", "text").option("--no-cleanup", "Keep cloned repo (for debugging)").option("--skip-install", "Skip dependency installation (faster, but may limit type resolution)").option("--skip-resolve", "Skip external type resolution from node_modules").option("--save-spec <path>", "Save full OpenPkg spec to file").action(async (url, options) => {
1909
- let tempDir;
1910
- try {
1911
- const parsed = parseGitHubUrl(url, options.ref ?? "main");
1912
- const cloneUrl = buildCloneUrl(parsed);
1913
- const displayUrl = buildDisplayUrl(parsed);
1914
- log("");
1915
- log(chalk6.bold(`Scanning ${displayUrl}`));
1916
- log(chalk6.gray(`Branch/tag: ${parsed.ref}`));
1917
- log("");
1918
- tempDir = path8.join(os.tmpdir(), `doccov-scan-${Date.now()}-${Math.random().toString(36).slice(2)}`);
1919
- fs6.mkdirSync(tempDir, { recursive: true });
1920
- process.stdout.write(chalk6.cyan(`> Cloning ${parsed.owner}/${parsed.repo}...
1921
- `));
1922
- try {
1923
- const git = simpleGit({
1924
- timeout: {
1925
- block: 30000
1926
- }
1927
- });
1928
- const originalEnv = { ...process.env };
1929
- process.env.GIT_TERMINAL_PROMPT = "0";
1930
- process.env.GIT_ASKPASS = "echo";
1931
- try {
1932
- await git.clone(cloneUrl, tempDir, [
1933
- "--depth",
1934
- "1",
1935
- "--branch",
1936
- parsed.ref,
1937
- "--single-branch"
1938
- ]);
1939
- } finally {
1940
- process.env = originalEnv;
1941
- }
1942
- process.stdout.write(chalk6.green(`✓ Cloned ${parsed.owner}/${parsed.repo}
1943
- `));
1944
- } catch (cloneError) {
1945
- process.stdout.write(chalk6.red(`✗ Failed to clone repository
1946
- `));
1947
- const message = cloneError instanceof Error ? cloneError.message : String(cloneError);
1948
- if (message.includes("Authentication failed") || message.includes("could not read Username") || message.includes("terminal prompts disabled") || message.includes("Invalid username or password") || message.includes("Permission denied")) {
1949
- throw new Error(`Authentication required: This repository appears to be private. ` + `Public repositories only are currently supported.
1950
- ` + `Repository: ${displayUrl}`);
1951
- }
1952
- if (message.includes("not found") || message.includes("404")) {
1953
- throw new Error(`Repository not accessible or does not exist: ${displayUrl}
1954
- ` + `Note: Private repositories are not currently supported.`);
1955
- }
1956
- if (message.includes("Could not find remote branch")) {
1957
- throw new Error(`Branch or tag not found: ${parsed.ref}`);
1958
- }
1959
- throw new Error(`Clone failed: ${message}`);
1960
- }
1961
- const fileSystem = new NodeFileSystem3(tempDir);
1962
- if (options.skipInstall) {
1963
- log(chalk6.gray("Skipping dependency installation (--skip-install)"));
1964
- } else {
1965
- process.stdout.write(chalk6.cyan(`> Installing dependencies...
1966
- `));
1967
- const installErrors = [];
1968
- try {
1969
- const { execSync } = await import("node:child_process");
1970
- const pmInfo = await detectPackageManager(fileSystem);
1971
- const installCmd = getInstallCommand(pmInfo);
1972
- const cmdString = installCmd.join(" ");
1973
- let installed = false;
1974
- if (pmInfo.lockfile) {
1975
- try {
1976
- execSync(cmdString, {
1977
- cwd: tempDir,
1978
- stdio: "pipe",
1979
- timeout: 180000
1980
- });
1981
- installed = true;
1982
- } catch (cmdError) {
1983
- const stderr = cmdError?.stderr?.toString() ?? "";
1984
- const msg = cmdError instanceof Error ? cmdError.message : String(cmdError);
1985
- installErrors.push(`[${cmdString}] ${stderr.slice(0, 150) || msg.slice(0, 150)}`);
1986
- }
1987
- }
1988
- if (!installed) {
1989
- try {
1990
- execSync("bun install", {
1991
- cwd: tempDir,
1992
- stdio: "pipe",
1993
- timeout: 120000
1994
- });
1995
- installed = true;
1996
- } catch (bunError) {
1997
- const stderr = bunError?.stderr?.toString() ?? "";
1998
- const msg = bunError instanceof Error ? bunError.message : String(bunError);
1999
- installErrors.push(`[bun install] ${stderr.slice(0, 150) || msg.slice(0, 150)}`);
2000
- try {
2001
- execSync("npm install --legacy-peer-deps --ignore-scripts", {
2002
- cwd: tempDir,
2003
- stdio: "pipe",
2004
- timeout: 180000
2005
- });
2006
- installed = true;
2007
- } catch (npmError) {
2008
- const npmStderr = npmError?.stderr?.toString() ?? "";
2009
- const npmMsg = npmError instanceof Error ? npmError.message : String(npmError);
2010
- installErrors.push(`[npm install] ${npmStderr.slice(0, 150) || npmMsg.slice(0, 150)}`);
2011
- }
2012
- }
2013
- }
2014
- if (installed) {
2015
- process.stdout.write(chalk6.green(`✓ Dependencies installed
2016
- `));
2017
- } else {
2018
- process.stdout.write(chalk6.yellow(`⚠ Could not install dependencies (analysis may be limited)
2019
- `));
2020
- for (const err of installErrors) {
2021
- log(chalk6.gray(` ${err}`));
2022
- }
2023
- }
2024
- } catch (outerError) {
2025
- const msg = outerError instanceof Error ? outerError.message : String(outerError);
2026
- process.stdout.write(chalk6.yellow(`⚠ Could not install dependencies: ${msg.slice(0, 100)}
2027
- `));
2028
- for (const err of installErrors) {
2029
- log(chalk6.gray(` ${err}`));
2030
- }
2031
- }
2032
- }
2033
- let targetDir = tempDir;
2034
- let packageName;
2035
- const mono = await detectMonorepo(fileSystem);
2036
- if (mono.isMonorepo) {
2037
- if (!options.package) {
2038
- error("");
2039
- error(chalk6.red(`Monorepo detected with ${mono.packages.length} packages. Specify target with --package:`));
2040
- error("");
2041
- error(formatPackageList(mono.packages));
2042
- error("");
2043
- throw new Error("Monorepo requires --package flag");
2044
- }
2045
- const pkg = findPackageByName(mono.packages, options.package);
2046
- if (!pkg) {
2047
- error("");
2048
- error(chalk6.red(`Package "${options.package}" not found. Available packages:`));
2049
- error("");
2050
- error(formatPackageList(mono.packages));
2051
- error("");
2052
- throw new Error(`Package not found: ${options.package}`);
2053
- }
2054
- targetDir = path8.join(tempDir, pkg.path);
2055
- packageName = pkg.name;
2056
- log(chalk6.gray(`Analyzing package: ${packageName}`));
2057
- }
2058
- process.stdout.write(chalk6.cyan(`> Detecting entry point...
2059
- `));
2060
- let entryPath;
2061
- const targetFs = mono.isMonorepo ? new NodeFileSystem3(targetDir) : fileSystem;
2062
- let buildFailed = false;
2063
- const runLlmFallback = async (reason) => {
2064
- process.stdout.write(chalk6.cyan(`> ${reason}, trying LLM fallback...
2065
- `));
2066
- const plan = await generateBuildPlan(targetDir);
2067
- if (!plan) {
2068
- return null;
2069
- }
2070
- if (plan.buildCommands.length > 0) {
2071
- const { execSync } = await import("node:child_process");
2072
- for (const cmd of plan.buildCommands) {
2073
- log(chalk6.gray(` Running: ${cmd}`));
2074
- try {
2075
- execSync(cmd, { cwd: targetDir, stdio: "pipe", timeout: 300000 });
2076
- } catch (buildError) {
2077
- buildFailed = true;
2078
- const msg = buildError instanceof Error ? buildError.message : String(buildError);
2079
- if (msg.includes("rustc") || msg.includes("cargo") || msg.includes("wasm-pack")) {
2080
- log(chalk6.yellow(` ⚠ Build requires Rust toolchain (not available)`));
2081
- } else if (msg.includes("rimraf") || msg.includes("command not found")) {
2082
- log(chalk6.yellow(` ⚠ Build failed: missing dependencies`));
2083
- } else {
2084
- log(chalk6.yellow(` ⚠ Build failed: ${msg.slice(0, 80)}`));
2085
- }
2086
- }
2087
- }
2088
- }
2089
- if (plan.notes) {
2090
- log(chalk6.gray(` Note: ${plan.notes}`));
2091
- }
2092
- return plan.entryPoint;
2093
- };
2094
- try {
2095
- const entry = await detectEntryPoint(targetFs);
2096
- const buildInfo = await detectBuildInfo(targetFs);
2097
- const needsBuildStep = entry.isDeclarationOnly && buildInfo.exoticIndicators.wasm;
2098
- if (needsBuildStep) {
2099
- process.stdout.write(chalk6.cyan(`> Detected .d.ts entry with WASM indicators...
2100
- `));
2101
- const llmEntry = await runLlmFallback("WASM project detected");
2102
- if (llmEntry) {
2103
- entryPath = path8.join(targetDir, llmEntry);
2104
- if (buildFailed) {
2105
- process.stdout.write(chalk6.green(`✓ Entry point: ${llmEntry} (using pre-committed declarations)
2106
- `));
2107
- log(chalk6.gray(" Coverage may be limited - generated .d.ts files typically lack JSDoc"));
2108
- } else {
2109
- process.stdout.write(chalk6.green(`✓ Entry point: ${llmEntry} (from LLM fallback - WASM project)
2110
- `));
2111
- }
2112
- } else {
2113
- entryPath = path8.join(targetDir, entry.path);
2114
- process.stdout.write(chalk6.green(`✓ Entry point: ${entry.path} (from ${entry.source})
2115
- `));
2116
- log(chalk6.yellow(" ⚠ WASM project detected but no API key - analysis may be limited"));
2117
- }
2118
- } else {
2119
- entryPath = path8.join(targetDir, entry.path);
2120
- process.stdout.write(chalk6.green(`✓ Entry point: ${entry.path} (from ${entry.source})
2121
- `));
2122
- }
2123
- } catch (entryError) {
2124
- const llmEntry = await runLlmFallback("Heuristics failed");
2125
- if (llmEntry) {
2126
- entryPath = path8.join(targetDir, llmEntry);
2127
- process.stdout.write(chalk6.green(`✓ Entry point: ${llmEntry} (from LLM fallback)
2128
- `));
2129
- } else {
2130
- process.stdout.write(chalk6.red(`✗ Could not detect entry point (set OPENAI_API_KEY for smart fallback)
2131
- `));
2132
- throw entryError;
2133
- }
2134
- }
2135
- process.stdout.write(chalk6.cyan(`> Analyzing documentation coverage...
2136
- `));
2137
- let result;
2138
- try {
2139
- const resolveExternalTypes = !options.skipResolve;
2140
- const doccov = createDocCov({ resolveExternalTypes });
2141
- result = await doccov.analyzeFileWithDiagnostics(entryPath);
2142
- process.stdout.write(chalk6.green(`✓ Analysis complete
2143
- `));
2144
- } catch (analysisError) {
2145
- process.stdout.write(chalk6.red(`✗ Analysis failed
2146
- `));
2147
- throw analysisError;
2148
- }
2149
- const spec = result.spec;
2150
- if (options.saveSpec) {
2151
- const specPath = path8.resolve(process.cwd(), options.saveSpec);
2152
- fs6.writeFileSync(specPath, JSON.stringify(spec, null, 2));
2153
- log(chalk6.green(`✓ Saved spec to ${options.saveSpec}`));
2154
- }
2155
- const summary = extractSpecSummary(spec);
2156
- const scanResult = {
2157
- owner: parsed.owner,
2158
- repo: parsed.repo,
2159
- ref: parsed.ref,
2160
- packageName,
2161
- coverage: summary.coverage,
2162
- exportCount: summary.exportCount,
2163
- typeCount: summary.typeCount,
2164
- driftCount: summary.driftCount,
2165
- undocumented: summary.undocumented,
2166
- drift: summary.drift
2167
- };
2168
- if (options.output === "json") {
2169
- log(JSON.stringify(scanResult, null, 2));
2170
- } else {
2171
- printTextResult(scanResult, log);
2172
- }
2173
- } catch (commandError) {
2174
- error(chalk6.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
2175
- process.exitCode = 1;
2176
- } finally {
2177
- if (tempDir && options.cleanup !== false) {
2178
- fsPromises.rm(tempDir, { recursive: true, force: true }).catch(() => {});
2179
- } else if (tempDir) {
2180
- log(chalk6.gray(`Repo preserved at: ${tempDir}`));
2181
- }
2182
- }
2183
- });
2184
- }
2185
- function categorizeDriftIssues(drift) {
2186
- const byCategory = {
2187
- structural: [],
2188
- semantic: [],
2189
- example: []
2190
- };
2191
- for (const d of drift) {
2192
- const category = DRIFT_CATEGORIES3[d.type] ?? "semantic";
2193
- byCategory[category].push(d);
2194
- }
2195
- return {
2196
- summary: {
2197
- total: drift.length,
2198
- byCategory: {
2199
- structural: byCategory.structural.length,
2200
- semantic: byCategory.semantic.length,
2201
- example: byCategory.example.length
2202
- }
2203
- },
2204
- byCategory
2205
- };
2206
- }
2207
- function formatDriftSummary(summary) {
2208
- if (summary.total === 0) {
2209
- return "No drift detected";
2210
- }
2211
- const parts = [];
2212
- if (summary.byCategory.structural > 0) {
2213
- parts.push(`${summary.byCategory.structural} structural`);
2214
- }
2215
- if (summary.byCategory.semantic > 0) {
2216
- parts.push(`${summary.byCategory.semantic} semantic`);
2217
- }
2218
- if (summary.byCategory.example > 0) {
2219
- parts.push(`${summary.byCategory.example} example`);
2220
- }
2221
- return `${summary.total} issues (${parts.join(", ")})`;
2222
- }
2223
- function printTextResult(result, log) {
2224
- log("");
2225
- log(chalk6.bold("DocCov Scan Results"));
2226
- log("─".repeat(40));
2227
- const repoName = result.packageName ? `${result.owner}/${result.repo} (${result.packageName})` : `${result.owner}/${result.repo}`;
2228
- log(`Repository: ${chalk6.cyan(repoName)}`);
2229
- log(`Branch: ${chalk6.gray(result.ref)}`);
2230
- log("");
2231
- const coverageColor = result.coverage >= 80 ? chalk6.green : result.coverage >= 50 ? chalk6.yellow : chalk6.red;
2232
- log(chalk6.bold("Coverage"));
2233
- log(` ${coverageColor(`${result.coverage}%`)}`);
2234
- log("");
2235
- log(chalk6.bold("Stats"));
2236
- log(` ${result.exportCount} exports`);
2237
- log(` ${result.typeCount} types`);
2238
- log(` ${result.undocumented.length} undocumented`);
2239
- const categorized = categorizeDriftIssues(result.drift);
2240
- const driftColor = result.driftCount > 0 ? chalk6.yellow : chalk6.green;
2241
- log(` ${driftColor(formatDriftSummary(categorized.summary))}`);
2242
- if (result.undocumented.length > 0) {
2243
- log("");
2244
- log(chalk6.bold("Undocumented Exports"));
2245
- for (const name of result.undocumented.slice(0, 10)) {
2246
- log(chalk6.yellow(` ! ${name}`));
2247
- }
2248
- if (result.undocumented.length > 10) {
2249
- log(chalk6.gray(` ... and ${result.undocumented.length - 10} more`));
2250
- }
2251
- }
2252
- if (result.drift.length > 0) {
2253
- log("");
2254
- log(chalk6.bold("Drift Issues"));
2255
- const categories = ["structural", "semantic", "example"];
2256
- for (const category of categories) {
2257
- const issues = categorized.byCategory[category];
2258
- if (issues.length === 0)
2259
- continue;
2260
- const label = DRIFT_CATEGORY_LABELS2[category];
2261
- log("");
2262
- log(chalk6.dim(` ${label} (${issues.length})`));
2263
- for (const d of issues.slice(0, 3)) {
2264
- log(chalk6.red(` • ${d.export}: ${d.issue}`));
2265
- }
2266
- if (issues.length > 3) {
2267
- log(chalk6.gray(` ... and ${issues.length - 3} more`));
2268
- }
2269
- }
2270
- }
2271
- log("");
2272
- }
1763
+ import { DocCov as DocCov3, NodeFileSystem as NodeFileSystem3, resolveTarget as resolveTarget3 } from "@doccov/sdk";
1764
+ import { normalize, validateSpec } from "@openpkg-ts/spec";
1765
+ // package.json
1766
+ var version = "0.13.0";
2273
1767
 
2274
1768
  // src/commands/spec.ts
2275
- import * as fs7 from "node:fs";
2276
- import * as path9 from "node:path";
2277
- import { DocCov as DocCov4, NodeFileSystem as NodeFileSystem4, resolveTarget as resolveTarget3 } from "@doccov/sdk";
2278
- import { normalize, validateSpec } from "@openpkg-ts/spec";
2279
- import chalk8 from "chalk";
1769
+ import chalk7 from "chalk";
2280
1770
 
2281
1771
  // src/utils/filter-options.ts
2282
1772
  import { mergeFilters, parseListFlag } from "@doccov/sdk";
2283
- import chalk7 from "chalk";
2284
- var formatList = (label, values) => `${label}: ${values.map((value) => chalk7.cyan(value)).join(", ")}`;
1773
+ import chalk6 from "chalk";
1774
+ var formatList = (label, values) => `${label}: ${values.map((value) => chalk6.cyan(value)).join(", ")}`;
2285
1775
  var mergeFilterOptions = (config, cliOptions) => {
2286
1776
  const messages = [];
2287
1777
  if (config?.include) {
@@ -2310,9 +1800,9 @@ var mergeFilterOptions = (config, cliOptions) => {
2310
1800
  };
2311
1801
 
2312
1802
  // src/commands/spec.ts
2313
- var defaultDependencies5 = {
2314
- createDocCov: (options) => new DocCov4(options),
2315
- writeFileSync: fs7.writeFileSync,
1803
+ var defaultDependencies4 = {
1804
+ createDocCov: (options) => new DocCov3(options),
1805
+ writeFileSync: fs5.writeFileSync,
2316
1806
  log: console.log,
2317
1807
  error: console.error
2318
1808
  };
@@ -2321,19 +1811,19 @@ function getArrayLength(value) {
2321
1811
  }
2322
1812
  function formatDiagnosticOutput(prefix, diagnostic, baseDir) {
2323
1813
  const location = diagnostic.location;
2324
- const relativePath = location?.file ? path9.relative(baseDir, location.file) || location.file : undefined;
2325
- const locationText = location && relativePath ? chalk8.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
1814
+ const relativePath = location?.file ? path7.relative(baseDir, location.file) || location.file : undefined;
1815
+ const locationText = location && relativePath ? chalk7.gray(`${relativePath}:${location.line ?? 1}:${location.column ?? 1}`) : null;
2326
1816
  const locationPrefix = locationText ? `${locationText} ` : "";
2327
1817
  return `${prefix} ${locationPrefix}${diagnostic.message}`;
2328
1818
  }
2329
1819
  function registerSpecCommand(program, dependencies = {}) {
2330
- const { createDocCov, writeFileSync: writeFileSync5, log, error } = {
2331
- ...defaultDependencies5,
1820
+ const { createDocCov, writeFileSync: writeFileSync4, log, error } = {
1821
+ ...defaultDependencies4,
2332
1822
  ...dependencies
2333
1823
  };
2334
- 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").action(async (entry, options) => {
1824
+ 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) => {
2335
1825
  try {
2336
- const fileSystem = new NodeFileSystem4(options.cwd);
1826
+ const fileSystem = new NodeFileSystem3(options.cwd);
2337
1827
  const resolved = await resolveTarget3(fileSystem, {
2338
1828
  cwd: options.cwd,
2339
1829
  package: options.package,
@@ -2341,19 +1831,19 @@ function registerSpecCommand(program, dependencies = {}) {
2341
1831
  });
2342
1832
  const { targetDir, entryFile, packageInfo, entryPointInfo } = resolved;
2343
1833
  if (packageInfo) {
2344
- log(chalk8.gray(`Found package at ${packageInfo.path}`));
1834
+ log(chalk7.gray(`Found package at ${packageInfo.path}`));
2345
1835
  }
2346
1836
  if (!entry) {
2347
- log(chalk8.gray(`Auto-detected entry point: ${entryPointInfo.path} (from ${entryPointInfo.source})`));
1837
+ log(chalk7.gray(`Auto-detected entry point: ${entryPointInfo.path} (from ${entryPointInfo.source})`));
2348
1838
  }
2349
1839
  let config = null;
2350
1840
  try {
2351
1841
  config = await loadDocCovConfig(targetDir);
2352
1842
  if (config?.filePath) {
2353
- log(chalk8.gray(`Loaded configuration from ${path9.relative(targetDir, config.filePath)}`));
1843
+ log(chalk7.gray(`Loaded configuration from ${path7.relative(targetDir, config.filePath)}`));
2354
1844
  }
2355
1845
  } catch (configError) {
2356
- error(chalk8.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
1846
+ error(chalk7.red("Failed to load DocCov config:"), configError instanceof Error ? configError.message : configError);
2357
1847
  process.exit(1);
2358
1848
  }
2359
1849
  const cliFilters = {
@@ -2362,10 +1852,10 @@ function registerSpecCommand(program, dependencies = {}) {
2362
1852
  };
2363
1853
  const resolvedFilters = mergeFilterOptions(config, cliFilters);
2364
1854
  for (const message of resolvedFilters.messages) {
2365
- log(chalk8.gray(`${message}`));
1855
+ log(chalk7.gray(`${message}`));
2366
1856
  }
2367
1857
  const resolveExternalTypes = !options.skipResolve;
2368
- process.stdout.write(chalk8.cyan(`> Generating OpenPkg spec...
1858
+ process.stdout.write(chalk7.cyan(`> Generating OpenPkg spec...
2369
1859
  `));
2370
1860
  let result;
2371
1861
  try {
@@ -2375,22 +1865,33 @@ function registerSpecCommand(program, dependencies = {}) {
2375
1865
  useCache: options.cache !== false,
2376
1866
  cwd: options.cwd
2377
1867
  });
1868
+ const generationInput = {
1869
+ entryPoint: path7.relative(targetDir, entryFile),
1870
+ entryPointSource: entryPointInfo.source,
1871
+ isDeclarationOnly: entryPointInfo.isDeclarationOnly ?? false,
1872
+ generatorName: "@doccov/cli",
1873
+ generatorVersion: version,
1874
+ packageManager: packageInfo?.packageManager,
1875
+ isMonorepo: resolved.isMonorepo,
1876
+ targetPackage: packageInfo?.name
1877
+ };
2378
1878
  const analyzeOptions = resolvedFilters.include || resolvedFilters.exclude ? {
2379
1879
  filters: {
2380
1880
  include: resolvedFilters.include,
2381
1881
  exclude: resolvedFilters.exclude
2382
- }
2383
- } : {};
1882
+ },
1883
+ generationInput
1884
+ } : { generationInput };
2384
1885
  result = await doccov.analyzeFileWithDiagnostics(entryFile, analyzeOptions);
2385
1886
  if (result.fromCache) {
2386
- process.stdout.write(chalk8.gray(`> Using cached spec
1887
+ process.stdout.write(chalk7.gray(`> Using cached spec
2387
1888
  `));
2388
1889
  } else {
2389
- process.stdout.write(chalk8.green(`> Generated OpenPkg spec
1890
+ process.stdout.write(chalk7.green(`> Generated OpenPkg spec
2390
1891
  `));
2391
1892
  }
2392
1893
  } catch (generationError) {
2393
- process.stdout.write(chalk8.red(`> Failed to generate spec
1894
+ process.stdout.write(chalk7.red(`> Failed to generate spec
2394
1895
  `));
2395
1896
  throw generationError;
2396
1897
  }
@@ -2400,27 +1901,64 @@ function registerSpecCommand(program, dependencies = {}) {
2400
1901
  const normalized = normalize(result.spec);
2401
1902
  const validation = validateSpec(normalized);
2402
1903
  if (!validation.ok) {
2403
- error(chalk8.red("Spec failed schema validation"));
1904
+ error(chalk7.red("Spec failed schema validation"));
2404
1905
  for (const err of validation.errors) {
2405
- error(chalk8.red(`schema: ${err.instancePath || "/"} ${err.message}`));
1906
+ error(chalk7.red(`schema: ${err.instancePath || "/"} ${err.message}`));
2406
1907
  }
2407
1908
  process.exit(1);
2408
1909
  }
2409
- const outputPath = path9.resolve(process.cwd(), options.output);
2410
- writeFileSync5(outputPath, JSON.stringify(normalized, null, 2));
2411
- log(chalk8.green(`> Wrote ${options.output}`));
2412
- log(chalk8.gray(` ${getArrayLength(normalized.exports)} exports`));
2413
- log(chalk8.gray(` ${getArrayLength(normalized.types)} types`));
1910
+ const outputPath = path7.resolve(process.cwd(), options.output);
1911
+ writeFileSync4(outputPath, JSON.stringify(normalized, null, 2));
1912
+ log(chalk7.green(`> Wrote ${options.output}`));
1913
+ log(chalk7.gray(` ${getArrayLength(normalized.exports)} exports`));
1914
+ log(chalk7.gray(` ${getArrayLength(normalized.types)} types`));
1915
+ if (options.verbose && normalized.generation) {
1916
+ const gen = normalized.generation;
1917
+ log("");
1918
+ log(chalk7.bold("Generation Info"));
1919
+ log(chalk7.gray(` Timestamp: ${gen.timestamp}`));
1920
+ log(chalk7.gray(` Generator: ${gen.generator.name}@${gen.generator.version}`));
1921
+ log(chalk7.gray(` Entry point: ${gen.analysis.entryPoint}`));
1922
+ log(chalk7.gray(` Detected via: ${gen.analysis.entryPointSource}`));
1923
+ log(chalk7.gray(` Declaration only: ${gen.analysis.isDeclarationOnly ? "yes" : "no"}`));
1924
+ log(chalk7.gray(` External types: ${gen.analysis.resolvedExternalTypes ? "resolved" : "skipped"}`));
1925
+ if (gen.analysis.maxTypeDepth) {
1926
+ log(chalk7.gray(` Max type depth: ${gen.analysis.maxTypeDepth}`));
1927
+ }
1928
+ log("");
1929
+ log(chalk7.bold("Environment"));
1930
+ log(chalk7.gray(` node_modules: ${gen.environment.hasNodeModules ? "found" : "not found"}`));
1931
+ if (gen.environment.packageManager) {
1932
+ log(chalk7.gray(` Package manager: ${gen.environment.packageManager}`));
1933
+ }
1934
+ if (gen.environment.isMonorepo) {
1935
+ log(chalk7.gray(` Monorepo: yes`));
1936
+ }
1937
+ if (gen.environment.targetPackage) {
1938
+ log(chalk7.gray(` Target package: ${gen.environment.targetPackage}`));
1939
+ }
1940
+ if (gen.issues.length > 0) {
1941
+ log("");
1942
+ log(chalk7.bold("Issues"));
1943
+ for (const issue of gen.issues) {
1944
+ const prefix = issue.severity === "error" ? chalk7.red(">") : issue.severity === "warning" ? chalk7.yellow(">") : chalk7.cyan(">");
1945
+ log(`${prefix} [${issue.code}] ${issue.message}`);
1946
+ if (issue.suggestion) {
1947
+ log(chalk7.gray(` ${issue.suggestion}`));
1948
+ }
1949
+ }
1950
+ }
1951
+ }
2414
1952
  if (options.showDiagnostics && result.diagnostics.length > 0) {
2415
1953
  log("");
2416
- log(chalk8.bold("Diagnostics"));
1954
+ log(chalk7.bold("Diagnostics"));
2417
1955
  for (const diagnostic of result.diagnostics) {
2418
- const prefix = diagnostic.severity === "error" ? chalk8.red(">") : diagnostic.severity === "warning" ? chalk8.yellow(">") : chalk8.cyan(">");
1956
+ const prefix = diagnostic.severity === "error" ? chalk7.red(">") : diagnostic.severity === "warning" ? chalk7.yellow(">") : chalk7.cyan(">");
2419
1957
  log(formatDiagnosticOutput(prefix, diagnostic, targetDir));
2420
1958
  }
2421
1959
  }
2422
1960
  } catch (commandError) {
2423
- error(chalk8.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
1961
+ error(chalk7.red("Error:"), commandError instanceof Error ? commandError.message : commandError);
2424
1962
  process.exit(1);
2425
1963
  }
2426
1964
  });
@@ -2428,8 +1966,8 @@ function registerSpecCommand(program, dependencies = {}) {
2428
1966
 
2429
1967
  // src/cli.ts
2430
1968
  var __filename2 = fileURLToPath(import.meta.url);
2431
- var __dirname2 = path10.dirname(__filename2);
2432
- var packageJson = JSON.parse(readFileSync4(path10.join(__dirname2, "../package.json"), "utf-8"));
1969
+ var __dirname2 = path8.dirname(__filename2);
1970
+ var packageJson = JSON.parse(readFileSync3(path8.join(__dirname2, "../package.json"), "utf-8"));
2433
1971
  var program = new Command;
2434
1972
  program.name("doccov").description("DocCov - Documentation coverage and drift detection for TypeScript").version(packageJson.version);
2435
1973
  registerCheckCommand(program);
@@ -2437,7 +1975,6 @@ registerInfoCommand(program);
2437
1975
  registerSpecCommand(program);
2438
1976
  registerDiffCommand(program);
2439
1977
  registerInitCommand(program);
2440
- registerScanCommand(program);
2441
1978
  program.command("*", { hidden: true }).action(() => {
2442
1979
  program.outputHelp();
2443
1980
  });
@@ -1,22 +1,3 @@
1
- import { createRequire } from "node:module";
2
- var __create = Object.create;
3
- var __getProtoOf = Object.getPrototypeOf;
4
- var __defProp = Object.defineProperty;
5
- var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __hasOwnProp = Object.prototype.hasOwnProperty;
7
- var __toESM = (mod, isNodeMode, target) => {
8
- target = mod != null ? __create(__getProtoOf(mod)) : {};
9
- const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
- for (let key of __getOwnPropNames(mod))
11
- if (!__hasOwnProp.call(to, key))
12
- __defProp(to, key, {
13
- get: () => mod[key],
14
- enumerable: true
15
- });
16
- return to;
17
- };
18
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
-
20
1
  // src/config/doccov-config.ts
21
2
  import { access } from "node:fs/promises";
22
3
  import path from "node:path";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doccov/cli",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "DocCov CLI - Documentation coverage and drift detection for TypeScript",
5
5
  "keywords": [
6
6
  "typescript",
@@ -48,9 +48,9 @@
48
48
  "dependencies": {
49
49
  "@ai-sdk/anthropic": "^1.0.0",
50
50
  "@ai-sdk/openai": "^1.0.0",
51
- "@doccov/sdk": "^0.12.0",
51
+ "@doccov/sdk": "^0.13.0",
52
52
  "@inquirer/prompts": "^7.8.0",
53
- "@openpkg-ts/spec": "^0.8.0",
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",