91e75e620b
CI: cua-driver distro-compat matrix / debian:12 (glibc 2.36) (push) Has been cancelled
CI: SPDX Headers / Check SPDX headers (warn-only) (push) Has been cancelled
CD: Docs MCP Server / build (linux/amd64) (push) Has been cancelled
CD: Docs MCP Server / build (linux/arm64) (push) Has been cancelled
CD: Docs MCP Server / merge (push) Has been cancelled
CI: cua-driver distro-compat matrix / Resolve release version (push) Has been cancelled
CI: cua-driver distro-compat matrix / fedora:41 (glibc 2.40) (push) Has been cancelled
CI: cua-driver distro-compat matrix / rockylinux:9 (glibc 2.34) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:22.04 (glibc 2.35) (push) Has been cancelled
CI: cua-driver distro-compat matrix / ubuntu:24.04 (glibc 2.39) (push) Has been cancelled
CI: cua-driver distro-compat matrix / Distro compat summary (push) Has been cancelled
CI: Rust Linux unit / Rust Linux unit and compile (push) Has been cancelled
CI: Rust Windows unit / Rust Windows unit and compile (push) Has been cancelled
CI: Nix Linux Rust source / Nix / compositor build (push) Has been cancelled
CI: Nix Linux Rust source / Nix / driver package (push) Has been cancelled
CI: Nix Linux Rust source / Nix / Rust unit tests (push) Has been cancelled
682 lines
20 KiB
TypeScript
682 lines
20 KiB
TypeScript
#!/usr/bin/env npx tsx
|
|
|
|
/**
|
|
* Versioned Documentation Generator
|
|
*
|
|
* Generates API documentation for historical versions by checking out
|
|
* tagged versions from git and running the extraction.
|
|
*
|
|
* Usage:
|
|
* npx tsx scripts/docs-generators/generate-versioned-docs.ts # Generate all
|
|
* npx tsx scripts/docs-generators/generate-versioned-docs.ts --sdk=computer # Specific SDK
|
|
* npx tsx scripts/docs-generators/generate-versioned-docs.ts --list # List available versions
|
|
*/
|
|
|
|
import { execSync, spawnSync } from 'child_process';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
// ============================================================================
|
|
// Types
|
|
// ============================================================================
|
|
|
|
type ExtractionType = 'python-griffe' | 'swift-dump-docs' | 'typescript';
|
|
|
|
interface SDKConfig {
|
|
name: string;
|
|
displayName: string;
|
|
tagPrefix: string;
|
|
packageDir: string;
|
|
packageName: string;
|
|
outputDir: string;
|
|
extractionType: ExtractionType;
|
|
/** Skip versions below this major.minor (e.g. '0.2' skips 0.1.x) */
|
|
minVersion?: string;
|
|
/** Base docs directory (defaults to cua/reference under DOCS_BASE) */
|
|
docsBasePath?: string;
|
|
}
|
|
|
|
interface VersionInfo {
|
|
tag: string;
|
|
version: string;
|
|
majorMinor: string;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Configuration
|
|
// ============================================================================
|
|
|
|
const ROOT_DIR = path.resolve(__dirname, '../..');
|
|
const DOCS_BASE = path.join(ROOT_DIR, 'docs/content/docs');
|
|
const DOCS_DIR = path.join(DOCS_BASE, 'cua/reference');
|
|
const PYTHON_SCRIPT = path.join(__dirname, 'extract_python_docs.py');
|
|
|
|
const SDK_CONFIGS: SDKConfig[] = [
|
|
{
|
|
name: 'computer',
|
|
displayName: 'Computer SDK',
|
|
tagPrefix: 'computer-v',
|
|
packageDir: 'libs/python/computer/computer',
|
|
packageName: 'computer',
|
|
outputDir: 'computer-sdk',
|
|
extractionType: 'python-griffe',
|
|
},
|
|
{
|
|
name: 'agent',
|
|
displayName: 'Agent SDK',
|
|
tagPrefix: 'agent-v',
|
|
packageDir: 'libs/python/agent/agent',
|
|
packageName: 'agent',
|
|
outputDir: 'agent-sdk',
|
|
extractionType: 'python-griffe',
|
|
},
|
|
{
|
|
name: 'bench',
|
|
displayName: 'Cua Bench',
|
|
tagPrefix: 'bench-v',
|
|
packageDir: 'libs/cua-bench/cua_bench',
|
|
packageName: 'cua_bench',
|
|
outputDir: 'reference',
|
|
extractionType: 'python-griffe',
|
|
docsBasePath: 'cuabench/reference',
|
|
},
|
|
{
|
|
name: 'lume',
|
|
displayName: 'Lume',
|
|
tagPrefix: 'lume-v',
|
|
packageDir: 'libs/lume',
|
|
packageName: 'lume',
|
|
outputDir: 'reference/lume',
|
|
extractionType: 'swift-dump-docs',
|
|
minVersion: '0.2',
|
|
docsBasePath: 'reference/lume',
|
|
},
|
|
{
|
|
name: 'cuabot',
|
|
displayName: 'Cua-Bot',
|
|
tagPrefix: 'cuabot-v',
|
|
packageDir: 'libs/cuabot/src',
|
|
packageName: 'cuabot',
|
|
outputDir: 'reference',
|
|
extractionType: 'typescript',
|
|
minVersion: '1.0',
|
|
docsBasePath: 'cuabot/reference',
|
|
},
|
|
];
|
|
|
|
// ============================================================================
|
|
// Main
|
|
// ============================================================================
|
|
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
const listOnly = args.includes('--list');
|
|
const sdkArg = args.find((a) => a.startsWith('--sdk='));
|
|
const targetSdk = sdkArg?.split('=')[1];
|
|
|
|
console.log('📚 Versioned Documentation Generator');
|
|
console.log('====================================\n');
|
|
|
|
// Check for uncommitted changes
|
|
const status = execSync('git status --porcelain', { encoding: 'utf-8', cwd: ROOT_DIR });
|
|
if (status.trim()) {
|
|
console.warn('⚠️ Warning: You have uncommitted changes. They will be preserved.\n');
|
|
}
|
|
|
|
for (const config of SDK_CONFIGS) {
|
|
if (targetSdk && targetSdk !== config.name) {
|
|
continue;
|
|
}
|
|
|
|
console.log(`\n📦 ${config.displayName}`);
|
|
console.log('─'.repeat(40));
|
|
|
|
// Get all version tags
|
|
const versions = getVersions(config.tagPrefix);
|
|
console.log(` Found ${versions.length} version tags`);
|
|
|
|
if (listOnly) {
|
|
// Just list versions
|
|
const grouped = groupByMajorMinor(versions);
|
|
for (const [majorMinor, versionList] of Object.entries(grouped)) {
|
|
console.log(` v${majorMinor}.x: ${versionList.map((v) => v.version).join(', ')}`);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Group by major.minor (we only generate one doc per major.minor)
|
|
const grouped = groupByMajorMinor(versions);
|
|
|
|
for (const [majorMinor, versionList] of Object.entries(grouped)) {
|
|
// Skip versions below minVersion
|
|
if (config.minVersion && compareVersions(majorMinor, config.minVersion) < 0) {
|
|
console.log(` Skipping v${majorMinor} (below minVersion ${config.minVersion})`);
|
|
continue;
|
|
}
|
|
|
|
// Use the latest patch version for this major.minor
|
|
const latestVersion = versionList[0];
|
|
console.log(`\n Generating v${majorMinor} (from ${latestVersion.tag})...`);
|
|
|
|
try {
|
|
// Generate docs for this version
|
|
await generateVersionDocs(config, latestVersion, majorMinor);
|
|
const files =
|
|
config.extractionType === 'swift-dump-docs'
|
|
? 'cli-reference.mdx + http-api.mdx'
|
|
: 'api.mdx';
|
|
console.log(` ✅ Generated v${majorMinor}/${files}`);
|
|
} catch (error) {
|
|
console.error(` ❌ Failed: ${error}`);
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('\n✅ Versioned documentation generation complete!');
|
|
}
|
|
|
|
// ============================================================================
|
|
// Version Discovery
|
|
// ============================================================================
|
|
|
|
function getVersions(tagPrefix: string): VersionInfo[] {
|
|
try {
|
|
const output = execSync(`git tag | grep "^${tagPrefix}"`, {
|
|
encoding: 'utf-8',
|
|
cwd: ROOT_DIR,
|
|
});
|
|
|
|
return output
|
|
.trim()
|
|
.split('\n')
|
|
.filter(Boolean)
|
|
.map((tag) => {
|
|
const version = tag.replace(tagPrefix, '');
|
|
const parts = version.split('.');
|
|
const majorMinor = `${parts[0]}.${parts[1] || '0'}`;
|
|
return { tag, version, majorMinor };
|
|
})
|
|
.sort((a, b) => compareVersions(b.version, a.version)); // Descending
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function groupByMajorMinor(versions: VersionInfo[]): Record<string, VersionInfo[]> {
|
|
const grouped: Record<string, VersionInfo[]> = {};
|
|
|
|
for (const v of versions) {
|
|
if (!grouped[v.majorMinor]) {
|
|
grouped[v.majorMinor] = [];
|
|
}
|
|
grouped[v.majorMinor].push(v);
|
|
}
|
|
|
|
// Sort keys descending
|
|
const sortedKeys = Object.keys(grouped).sort((a, b) => compareVersions(b, a));
|
|
const sorted: Record<string, VersionInfo[]> = {};
|
|
for (const key of sortedKeys) {
|
|
sorted[key] = grouped[key];
|
|
}
|
|
|
|
return sorted;
|
|
}
|
|
|
|
function compareVersions(a: string, b: string): number {
|
|
const partsA = a.split('.').map((x) => parseInt(x, 10) || 0);
|
|
const partsB = b.split('.').map((x) => parseInt(x, 10) || 0);
|
|
|
|
for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
|
|
const partA = partsA[i] || 0;
|
|
const partB = partsB[i] || 0;
|
|
if (partA !== partB) {
|
|
return partA - partB;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// ============================================================================
|
|
// Documentation Generation
|
|
// ============================================================================
|
|
|
|
async function generateVersionDocs(
|
|
config: SDKConfig,
|
|
versionInfo: VersionInfo,
|
|
majorMinor: string
|
|
): Promise<void> {
|
|
// Resolve output directory based on config
|
|
const baseDir = config.docsBasePath
|
|
? path.join(DOCS_BASE, config.docsBasePath)
|
|
: path.join(DOCS_DIR, config.outputDir);
|
|
const outputDir = path.join(baseDir, `v${majorMinor}`);
|
|
|
|
// Create output directory
|
|
if (!fs.existsSync(outputDir)) {
|
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
}
|
|
|
|
// Checkout the package directory at the tagged version
|
|
const packagePath = path.join(ROOT_DIR, config.packageDir);
|
|
|
|
try {
|
|
// Save current state
|
|
execSync(`git stash push -m "versioned-docs-temp" -- ${config.packageDir}`, {
|
|
cwd: ROOT_DIR,
|
|
stdio: 'pipe',
|
|
});
|
|
} catch {
|
|
// No changes to stash, continue
|
|
}
|
|
|
|
try {
|
|
// Checkout tagged version of the package
|
|
execSync(`git checkout ${versionInfo.tag} -- ${config.packageDir}`, {
|
|
cwd: ROOT_DIR,
|
|
stdio: 'pipe',
|
|
});
|
|
|
|
if (config.extractionType === 'swift-dump-docs') {
|
|
await generateSwiftVersionDocs(config, versionInfo, majorMinor, outputDir);
|
|
} else if (config.extractionType === 'typescript') {
|
|
await generateTypeScriptVersionDocs(config, versionInfo, majorMinor, outputDir);
|
|
} else {
|
|
await generatePythonVersionDocs(config, versionInfo, majorMinor, outputDir);
|
|
}
|
|
} finally {
|
|
// Restore HEAD version
|
|
execSync(`git checkout HEAD -- ${config.packageDir}`, {
|
|
cwd: ROOT_DIR,
|
|
stdio: 'pipe',
|
|
});
|
|
|
|
// Restore stashed changes if any
|
|
try {
|
|
execSync('git stash pop', { cwd: ROOT_DIR, stdio: 'pipe' });
|
|
} catch {
|
|
// No stash to pop
|
|
}
|
|
}
|
|
}
|
|
|
|
async function generatePythonVersionDocs(
|
|
config: SDKConfig,
|
|
versionInfo: VersionInfo,
|
|
majorMinor: string,
|
|
outputDir: string
|
|
): Promise<void> {
|
|
const packagePath = path.join(ROOT_DIR, config.packageDir);
|
|
const outputPath = path.join(outputDir, 'api.mdx');
|
|
|
|
// Run extraction
|
|
const extractOutput = execSync(
|
|
`python3 "${PYTHON_SCRIPT}" "${packagePath}" "${config.packageName}"`,
|
|
{ encoding: 'utf-8', cwd: ROOT_DIR, maxBuffer: 10 * 1024 * 1024 }
|
|
);
|
|
|
|
const docs = JSON.parse(extractOutput);
|
|
|
|
// Generate MDX
|
|
const mdx = generateMDX(docs, config, versionInfo, majorMinor);
|
|
|
|
// Write output
|
|
fs.writeFileSync(outputPath, mdx);
|
|
|
|
// Create meta.json for the version folder
|
|
const metaPath = path.join(outputDir, 'meta.json');
|
|
fs.writeFileSync(
|
|
metaPath,
|
|
JSON.stringify(
|
|
{
|
|
title: `v${majorMinor}`,
|
|
description: `${config.displayName} v${majorMinor} API Reference`,
|
|
pages: ['api'],
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
);
|
|
}
|
|
|
|
async function generateSwiftVersionDocs(
|
|
config: SDKConfig,
|
|
versionInfo: VersionInfo,
|
|
majorMinor: string,
|
|
outputDir: string
|
|
): Promise<void> {
|
|
const lumeDir = path.join(ROOT_DIR, config.packageDir);
|
|
|
|
// Build Lume at this version
|
|
try {
|
|
execSync('swift build -c release', {
|
|
cwd: lumeDir,
|
|
stdio: 'pipe',
|
|
timeout: 300000, // 5 min build timeout
|
|
});
|
|
} catch (error) {
|
|
throw new Error(`Swift build failed for ${versionInfo.tag}: ${error}`);
|
|
}
|
|
|
|
// Import Lume MDX generators
|
|
const { generateCLIReferenceMDX, generateHTTPAPIMDX } = await import('./lume');
|
|
|
|
// Extract CLI docs
|
|
let cliMdx: string;
|
|
try {
|
|
const cliDocsJson = execSync('.build/release/lume dump-docs --type cli', {
|
|
cwd: lumeDir,
|
|
encoding: 'utf-8',
|
|
});
|
|
const cliDocs = JSON.parse(cliDocsJson);
|
|
cliMdx = generateVersionedLumeMDX(
|
|
generateCLIReferenceMDX(cliDocs),
|
|
config,
|
|
versionInfo,
|
|
majorMinor,
|
|
'CLI Reference'
|
|
);
|
|
} catch (error) {
|
|
throw new Error(`CLI docs extraction failed for ${versionInfo.tag}: ${error}`);
|
|
}
|
|
|
|
// Extract API docs
|
|
let apiMdx: string;
|
|
try {
|
|
const apiDocsJson = execSync('.build/release/lume dump-docs --type api', {
|
|
cwd: lumeDir,
|
|
encoding: 'utf-8',
|
|
});
|
|
const apiDocs = JSON.parse(apiDocsJson);
|
|
apiMdx = generateVersionedLumeMDX(
|
|
generateHTTPAPIMDX(apiDocs),
|
|
config,
|
|
versionInfo,
|
|
majorMinor,
|
|
'HTTP API Reference'
|
|
);
|
|
} catch (error) {
|
|
throw new Error(`API docs extraction failed for ${versionInfo.tag}: ${error}`);
|
|
}
|
|
|
|
// Write outputs
|
|
fs.writeFileSync(path.join(outputDir, 'cli-reference.mdx'), cliMdx);
|
|
fs.writeFileSync(path.join(outputDir, 'http-api.mdx'), apiMdx);
|
|
|
|
// Create meta.json
|
|
fs.writeFileSync(
|
|
path.join(outputDir, 'meta.json'),
|
|
JSON.stringify(
|
|
{
|
|
title: `v${majorMinor}`,
|
|
description: `${config.displayName} v${majorMinor} Reference`,
|
|
pages: ['cli-reference', 'http-api'],
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
);
|
|
}
|
|
|
|
async function generateTypeScriptVersionDocs(
|
|
config: SDKConfig,
|
|
versionInfo: VersionInfo,
|
|
majorMinor: string,
|
|
outputDir: string
|
|
): Promise<void> {
|
|
// Run the typescript-sdk generator to produce MDX, then wrap it with a version callout
|
|
const tsGenScript = path.join(__dirname, 'typescript-sdk.ts');
|
|
const outputPath = path.join(outputDir, 'api.mdx');
|
|
|
|
// Run the TS generator targeting cuabot — it will write to the configured output path
|
|
// Instead, we run it inline and capture the current version's output, then modify it
|
|
try {
|
|
// Generate using the typescript-sdk generator
|
|
spawnSync('npx', ['tsx', tsGenScript, `--sdk=${config.name}`], {
|
|
cwd: ROOT_DIR,
|
|
stdio: 'pipe',
|
|
});
|
|
|
|
// Read the generated file and wrap it with version callout
|
|
const generatedPath = path.join(ROOT_DIR, 'docs/content/docs/cuabot/reference/index.mdx');
|
|
if (fs.existsSync(generatedPath)) {
|
|
let content = fs.readFileSync(generatedPath, 'utf-8');
|
|
|
|
// Replace VersionHeader with old-version callout
|
|
const lines = content.split('\n');
|
|
const result: string[] = [];
|
|
let skipVersionHeader = false;
|
|
|
|
for (const line of lines) {
|
|
if (line.startsWith('<VersionHeader')) {
|
|
skipVersionHeader = true;
|
|
result.push('<Callout type="warn">');
|
|
result.push(
|
|
` This is documentation for **v${majorMinor}**. [View latest version](/cuabot/reference).`
|
|
);
|
|
result.push('</Callout>');
|
|
result.push('');
|
|
result.push('<div className="flex items-center gap-2 mb-6">');
|
|
result.push(
|
|
` <span className="px-2 py-1 bg-amber-100 dark:bg-amber-900 text-amber-800 dark:text-amber-200 rounded text-sm font-mono">v${versionInfo.version}</span>`
|
|
);
|
|
result.push(
|
|
` <span className="text-sm text-fd-muted-foreground">npm install -g cuabot@${versionInfo.version}</span>`
|
|
);
|
|
result.push('</div>');
|
|
result.push('');
|
|
continue;
|
|
}
|
|
if (skipVersionHeader) {
|
|
if (line.startsWith('/>')) {
|
|
skipVersionHeader = false;
|
|
continue;
|
|
}
|
|
continue;
|
|
}
|
|
result.push(line);
|
|
}
|
|
|
|
// Update title
|
|
content = result
|
|
.join('\n')
|
|
.replace(/^title: .*$/m, `title: ${config.displayName} v${majorMinor} API Reference`);
|
|
|
|
fs.writeFileSync(outputPath, content);
|
|
}
|
|
} catch (error) {
|
|
throw new Error(`TypeScript docs generation failed for ${versionInfo.tag}: ${error}`);
|
|
}
|
|
|
|
// Create meta.json
|
|
fs.writeFileSync(
|
|
path.join(outputDir, 'meta.json'),
|
|
JSON.stringify(
|
|
{
|
|
title: `v${majorMinor}`,
|
|
description: `${config.displayName} v${majorMinor} API Reference`,
|
|
pages: ['api'],
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Wrap generated Lume MDX with a version warning callout for historical versions.
|
|
* Replaces the VersionHeader (which shows current version) with an old-version notice.
|
|
*/
|
|
function generateVersionedLumeMDX(
|
|
currentMdx: string,
|
|
config: SDKConfig,
|
|
versionInfo: VersionInfo,
|
|
majorMinor: string,
|
|
title: string
|
|
): string {
|
|
// Replace the VersionHeader block with an old-version callout
|
|
const lines = currentMdx.split('\n');
|
|
const result: string[] = [];
|
|
let skipVersionHeader = false;
|
|
|
|
for (const line of lines) {
|
|
// Skip the VersionHeader block
|
|
if (line.startsWith('<VersionHeader')) {
|
|
skipVersionHeader = true;
|
|
// Insert old-version callout instead
|
|
result.push('<Callout type="warn">');
|
|
result.push(
|
|
` This is documentation for **v${majorMinor}**. [View latest version](/reference/lume/cli-reference).`
|
|
);
|
|
result.push('</Callout>');
|
|
result.push('');
|
|
result.push('<div className="flex items-center gap-2 mb-6">');
|
|
result.push(
|
|
` <span className="px-2 py-1 bg-amber-100 dark:bg-amber-900 text-amber-800 dark:text-amber-200 rounded text-sm font-mono">v${versionInfo.version}</span>`
|
|
);
|
|
result.push(
|
|
` <span className="text-sm text-fd-muted-foreground">curl -fsSL .../install.sh | bash</span>`
|
|
);
|
|
result.push('</div>');
|
|
result.push('');
|
|
continue;
|
|
}
|
|
if (skipVersionHeader) {
|
|
if (line.startsWith('/>')) {
|
|
skipVersionHeader = false;
|
|
continue;
|
|
}
|
|
continue;
|
|
}
|
|
result.push(line);
|
|
}
|
|
|
|
return result.join('\n');
|
|
}
|
|
|
|
// ============================================================================
|
|
// MDX Generation (simplified version for historical docs)
|
|
// ============================================================================
|
|
|
|
function generateMDX(
|
|
docs: any,
|
|
config: SDKConfig,
|
|
versionInfo: VersionInfo,
|
|
majorMinor: string
|
|
): string {
|
|
const lines: string[] = [];
|
|
|
|
// Frontmatter
|
|
lines.push('---');
|
|
lines.push(`title: ${config.displayName} v${majorMinor} API Reference`);
|
|
lines.push(`description: API reference for ${config.displayName} version ${majorMinor}`);
|
|
lines.push('---');
|
|
lines.push('');
|
|
|
|
// Auto-generated notice
|
|
lines.push(`{/*`);
|
|
lines.push(` AUTO-GENERATED FILE - DO NOT EDIT DIRECTLY`);
|
|
lines.push(` Generated by: npx tsx scripts/docs-generators/generate-versioned-docs.ts`);
|
|
lines.push(` Source tag: ${versionInfo.tag}`);
|
|
lines.push(` Version: ${versionInfo.version}`);
|
|
lines.push(`*/}`);
|
|
lines.push('');
|
|
|
|
// Imports
|
|
lines.push("import { Callout } from 'fumadocs-ui/components/callout';");
|
|
lines.push('');
|
|
|
|
// Version notice — link to the folder root (index.mdx is the landing page)
|
|
const latestHref = config.docsBasePath
|
|
? `/${config.docsBasePath.replace(/\/$/, '')}/${config.outputDir}`
|
|
: `/cua/reference/${config.outputDir}`;
|
|
lines.push('<Callout type="warn">');
|
|
lines.push(
|
|
` This is documentation for **v${majorMinor}**. [View latest version](${latestHref}).`
|
|
);
|
|
lines.push('</Callout>');
|
|
lines.push('');
|
|
|
|
// Version badge
|
|
const pipName = config.packageName.replace(/_/g, '-');
|
|
const fullPipName = pipName.startsWith('cua') ? pipName : `cua-${pipName}`;
|
|
lines.push('<div className="flex items-center gap-2 mb-6">');
|
|
lines.push(
|
|
` <span className="px-2 py-1 bg-amber-100 dark:bg-amber-900 text-amber-800 dark:text-amber-200 rounded text-sm font-mono">v${versionInfo.version}</span>`
|
|
);
|
|
lines.push(
|
|
` <span className="text-sm text-fd-muted-foreground">pip install ${fullPipName}==${versionInfo.version}</span>`
|
|
);
|
|
lines.push('</div>');
|
|
lines.push('');
|
|
|
|
// Package description
|
|
if (docs.docstring) {
|
|
lines.push(escapeMDX(docs.docstring));
|
|
lines.push('');
|
|
}
|
|
|
|
// Classes
|
|
if (docs.classes && docs.classes.length > 0) {
|
|
lines.push('## Classes');
|
|
lines.push('');
|
|
lines.push('| Class | Description |');
|
|
lines.push('|-------|-------------|');
|
|
for (const cls of docs.classes) {
|
|
if (!cls.is_private) {
|
|
const desc = escapeMDX(cls.description?.split('\n')[0]) || 'No description';
|
|
lines.push(`| \`${cls.name}\` | ${desc} |`);
|
|
}
|
|
}
|
|
lines.push('');
|
|
|
|
// Class details
|
|
for (const cls of docs.classes) {
|
|
if (!cls.is_private) {
|
|
lines.push(`## ${cls.name}`);
|
|
lines.push('');
|
|
if (cls.description) {
|
|
lines.push(escapeMDX(cls.description));
|
|
lines.push('');
|
|
}
|
|
|
|
// Methods
|
|
const publicMethods = (cls.methods || []).filter(
|
|
(m: any) => !m.is_private && !m.is_dunder && m.name !== '__init__'
|
|
);
|
|
if (publicMethods.length > 0) {
|
|
lines.push('### Methods');
|
|
lines.push('');
|
|
for (const method of publicMethods) {
|
|
lines.push(`#### ${cls.name}.${method.name}`);
|
|
lines.push('');
|
|
lines.push('```python');
|
|
lines.push(method.signature || `def ${method.name}(...)`);
|
|
lines.push('```');
|
|
lines.push('');
|
|
if (method.description) {
|
|
lines.push(escapeMDX(method.description));
|
|
lines.push('');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
function escapeMDX(text: string): string {
|
|
if (!text) return text;
|
|
return text.replace(/\{/g, '\\{').replace(/\}/g, '\\}');
|
|
}
|
|
|
|
// ============================================================================
|
|
// Run
|
|
// ============================================================================
|
|
|
|
main().catch((error) => {
|
|
console.error('Error:', error);
|
|
process.exit(1);
|
|
});
|