#!/usr/bin/env python3 """ Generate comprehensive CLI documentation from Click commands """ import sys from pathlib import Path # Add project root to Python path project_root = Path(__file__).parent.parent sys.path.insert(0, str(project_root)) # Import after path setup from src.cli.commands import app import click def format_option(option): """Format a Click option for documentation""" names = [] if option.opts: names.extend(option.opts) if option.secondary_opts: names.extend(option.secondary_opts) name_str = ", ".join(f"`{n}`" for n in names) # Get option type info type_info = "" if hasattr(option, 'type') and option.type: if isinstance(option.type, click.Choice): choices = ", ".join(f"`{c}`" for c in option.type.choices) type_info = f"Choice: {choices}" elif isinstance(option.type, click.Path): type_info = "Path" if hasattr(option.type, 'exists') and option.type.exists: type_info += " (must exist)" elif hasattr(option.type, '__name__'): type_info = option.type.__name__ else: type_info = str(option.type) # Get default value default_info = "" if option.is_flag: default_info = "Flag (boolean)" elif option.default is not None and not callable(option.default): # Filter out Click's Sentinel.UNSET default_val = str(option.default) if 'Sentinel' not in default_val and 'UNSET' not in default_val: default_info = f"Default: `{option.default}`" # Required indicator required = option.required return { 'name': name_str, 'help': option.help or "", 'type': type_info, 'default': default_info, 'required': required } def format_command(cmd): """Format a Click command for documentation""" if not isinstance(cmd, click.Command): return None doc = { 'name': cmd.name, 'help': cmd.get_short_help_str() or cmd.help or "", 'description': cmd.help or "", 'options': [] } # Get all options for param in cmd.params: if isinstance(param, click.Option): doc['options'].append(format_option(param)) return doc def generate_docs(): """Generate comprehensive CLI documentation""" commands = [] for name, cmd in app.commands.items(): cmd_doc = format_command(cmd) if cmd_doc: commands.append(cmd_doc) # Sort commands alphabetically commands.sort(key=lambda x: x['name']) # Group commands by category categories = { 'System': ['config', 'health', 'models'], 'User Management': ['add-user', 'delete-user', 'list-users'], 'Site Management': ['provision-site', 'attach-domain', 'list-sites', 'get-site', 'remove-site', 'sync-sites'], 'Project Management': ['ingest-cora', 'ingest-simple', 'list-projects'], 'Content Generation': ['generate-batch'], 'Deployment': ['deploy-batch', 'verify-deployment'], 'Link Export': ['get-links'] } # Build markdown md_lines = [] md_lines.append("# CLI Command Reference") md_lines.append("") md_lines.append("Comprehensive documentation for all CLI commands.") md_lines.append("") md_lines.append("## Table of Contents") md_lines.append("") for category in categories.keys(): md_lines.append(f"- [{category}](#{category.lower().replace(' ', '-')})") md_lines.append("") md_lines.append("---") md_lines.append("") # Generate documentation for each category for category, command_names in categories.items(): md_lines.append(f"## {category}") md_lines.append("") for cmd_doc in commands: if cmd_doc['name'] in command_names: md_lines.append(f"### `{cmd_doc['name']}`") md_lines.append("") if cmd_doc['description']: md_lines.append(cmd_doc['description']) md_lines.append("") if cmd_doc['options']: md_lines.append("**Options:**") md_lines.append("") for opt in cmd_doc['options']: parts = [opt['name']] if opt['required']: parts.append("**(required)**") md_lines.append(f"- {' '.join(parts)}") details = [] if opt['type']: details.append(f"Type: {opt['type']}") if opt['default'] and 'Sentinel' not in opt['default']: details.append(opt['default']) if opt['help']: details.append(opt['help']) if details: md_lines.append(f" - {' | '.join(details)}") md_lines.append("") else: md_lines.append("No options required.") md_lines.append("") md_lines.append("**Example:**") md_lines.append("") md_lines.append("```bash") example_cmd = f"uv run python main.py {cmd_doc['name']}" # Build example with required options and common optional ones example_parts = [] for opt in cmd_doc['options']: opt_name = opt['name'].replace('`', '').split(',')[0].strip() if opt['required']: # Add required options with example values if '--username' in opt_name or '--admin-user' in opt_name: example_parts.append("--username admin") elif '--password' in opt_name or '--admin-password' in opt_name: example_parts.append("--password yourpass") elif '--file' in opt_name or '-f' in opt_name: example_parts.append("--file path/to/file.xlsx") elif '--job-file' in opt_name or '-j' in opt_name: example_parts.append("--job-file jobs/example.json") elif '--project-id' in opt_name or '-p' in opt_name: example_parts.append("--project-id 1") elif '--batch-id' in opt_name or '-b' in opt_name: example_parts.append("--batch-id 1") elif '--domain' in opt_name: example_parts.append("--domain www.example.com") elif '--name' in opt_name: example_parts.append("--name \"My Project\"") elif '--tier' in opt_name or '-t' in opt_name: example_parts.append("--tier 1") elif '--storage-name' in opt_name: example_parts.append("--storage-name my-storage-zone") elif '--region' in opt_name: example_parts.append("--region DE") else: example_parts.append(f"{opt_name} ") elif not opt['required'] and '--debug' in opt_name: # Include common flags in examples example_parts.append("--debug") if example_parts: example_cmd += " " + " ".join(example_parts) md_lines.append(example_cmd) md_lines.append("```") md_lines.append("") md_lines.append("---") md_lines.append("") # Add any commands not in categories uncategorized = [c for c in commands if not any(c['name'] in names for names in categories.values())] if uncategorized: md_lines.append("## Other Commands") md_lines.append("") for cmd_doc in uncategorized: md_lines.append(f"### `{cmd_doc['name']}`") md_lines.append("") if cmd_doc['description']: md_lines.append(cmd_doc['description']) md_lines.append("") return "\n".join(md_lines) if __name__ == "__main__": docs = generate_docs() output_file = Path(__file__).parent.parent / "docs" / "CLI_COMMAND_REFERENCE.md" output_file.parent.mkdir(exist_ok=True) with open(output_file, 'w', encoding='utf-8') as f: f.write(docs) print(f"CLI documentation generated: {output_file}") print(f"Total commands documented: {len(app.commands)}")