""" # Summary Command definitions for the doc-forge CLI. Provides the CLI structure using Click, including build, serve, and tree commands. """ from pathlib import Path import click from docforge.cli import mcp_utils, mkdocs_utils from docforge.loaders import GriffeLoader @click.group() def cli() -> None: """ Root command group for the doc-forge CLI. Provides commands for building, serving, and inspecting documentation generated from Python source code. """ pass @cli.command() @click.option("--mcp", is_flag=True, help="Build MCP resources") @click.option("--mkdocs", is_flag=True, help="Build MkDocs site") @click.option( "--module-is-source", is_flag=True, help="Module is source folder and to be treated as root folder", ) @click.option("--module", help="Python module to document") @click.option("--project-name", help="Project name override") @click.option("--site-name", help="MkDocs site name") @click.option( "--docs-dir", type=click.Path(path_type=Path), default=Path("docs"), help="Directory for MD sources", ) @click.option( "--nav", "nav_file", type=click.Path(path_type=Path), default=Path("docforge.nav.yml"), help="Nav spec path", ) @click.option( "--template", type=click.Path(path_type=Path), help="MkDocs template path" ) @click.option( "--mkdocs-yml", type=click.Path(path_type=Path), default=Path("mkdocs.yml"), help="Output config path", ) @click.option( "--out-dir", type=click.Path(path_type=Path), default=Path("mcp_docs"), help="MCP output directory", ) def build( mcp: bool, mkdocs: bool, module_is_source: bool, module: str | None, project_name: str | None, site_name: str | None, docs_dir: Path, nav_file: Path, template: Path | None, mkdocs_yml: Path, out_dir: Path, ) -> None: """ Build documentation artifacts. This command performs the full documentation build pipeline: 1. Introspects the Python project using Griffe 2. Generates renderer-specific documentation sources 3. Optionally builds the final documentation output Depending on the selected options, the build can target: - MkDocs static documentation sites - MCP structured documentation resources Args: mcp (bool): Enable MCP documentation generation. mkdocs (bool): Enable MkDocs documentation generation. module_is_source (bool): Treat the specified module directory as the project root. module (Optional[str]): Python module import path to document. project_name (Optional[str]): Optional override for the project name. site_name (Optional[str]): Display name for the MkDocs site. docs_dir (Path): Directory where Markdown documentation sources will be generated. nav_file (Path): Path to the navigation specification file. template (Optional[Path]): Optional custom MkDocs configuration template. mkdocs_yml (Path): Output path for the generated MkDocs configuration. out_dir (Path): Output directory for generated MCP resources. Raises: click.UsageError: If required options are missing or conflicting. """ if not mcp and not mkdocs: raise click.UsageError("Must specify either --mcp or --mkdocs") if mkdocs: if not module: raise click.UsageError("--module is required for MkDocs build") if not site_name: site_name = module click.echo(f"Generating MkDocs sources in {docs_dir}...") mkdocs_utils.generate_sources( module, docs_dir, project_name, module_is_source, ) click.echo(f"Generating MkDocs config {mkdocs_yml}...") mkdocs_utils.generate_config( docs_dir, nav_file, template, mkdocs_yml, site_name ) click.echo("Running MkDocs build...") mkdocs_utils.build(mkdocs_yml) click.echo("MkDocs build completed.") if mcp: if not module: raise click.UsageError("--module is required for MCP build") click.echo(f"Generating MCP resources in {out_dir}...") mcp_utils.generate_resources(module, project_name, out_dir) click.echo("MCP build completed.") @cli.command() @click.option("--mcp", is_flag=True, help="Serve MCP documentation") @click.option("--mkdocs", is_flag=True, help="Serve MkDocs site") @click.option("--module", help="Python module to serve") @click.option( "--mkdocs-yml", type=click.Path(path_type=Path), default=Path("mkdocs.yml"), help="MkDocs config path", ) @click.option( "--out-dir", type=click.Path(path_type=Path), default=Path("mcp_docs"), help="MCP root directory", ) def serve( mcp: bool, mkdocs: bool, module: str | None, mkdocs_yml: Path, out_dir: Path, ) -> None: """ Serve generated documentation locally. Depending on the selected mode, this command starts either: - A MkDocs development server for browsing documentation - An MCP server exposing structured documentation resources Args: mcp (bool): Serve documentation using the MCP server. mkdocs (bool): Serve the MkDocs development site. module (Optional[str]): Python module import path to serve via MCP. mkdocs_yml (Path): Path to the MkDocs configuration file. out_dir (Path): Root directory containing MCP documentation resources. Raises: click.UsageError: If invalid or conflicting options are provided. """ if mcp and mkdocs: raise click.UsageError("Cannot specify both --mcp and --mkdocs") if not mcp and not mkdocs: raise click.UsageError("Must specify either --mcp or --mkdocs") if mcp and not module: raise click.UsageError("--module is required for MCP serve") if mkdocs: mkdocs_utils.serve(mkdocs_yml) elif mcp: mcp_utils.serve(module, out_dir) @cli.command() @click.option( "--module", required=True, help="Python module import path to introspect", ) @click.option( "--project-name", help="Project name (defaults to specified module)", ) def tree( module: str, project_name: str | None, ) -> None: """ Display the documentation object tree for a module. This command introspects the specified module and prints a hierarchical representation of the discovered documentation objects, including modules, classes, functions, and members. Args: module (str): Python module import path to introspect. project_name (Optional[str]): Optional name to display as the project root. """ loader = GriffeLoader() project = loader.load_project([module], project_name) click.echo(project.name) for module in project.get_all_modules(): click.echo(f"├── {module.path}") for obj in module.get_all_objects(): _print_object(obj, indent="│ ") def _print_object(obj, indent: str) -> None: """ Recursively print a documentation object and its members. This helper function traverses the documentation object graph and prints each object with indentation to represent hierarchy. Args: obj: Documentation object to print. indent (str): Current indentation prefix used for nested members. """ click.echo(f"{indent}├── {obj.name}") for member in obj.get_all_members(): _print_object(member, indent + "│ ")