From 2ae96f58de24f9e0218cfe51520a36e2107028ec Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Sat, 12 Sep 2026 14:23:22 +0530 Subject: [PATCH] feat: build each doc kind with its own MkDocs config and site --- .gitignore | 1 + README.md | 44 +-- docforge/__init__.py | 44 +-- docforge/cli/commands.py | 205 ++++++++------ docforge/cli/commands.pyi | 4 +- docforge/cli/mkdocs_utils.py | 256 ++++++++++++------ docforge/cli/mkdocs_utils.pyi | 22 +- docs/mcp/modules/docforge.cli.commands.json | 83 ++++-- docs/mcp/modules/docforge.cli.json | 151 ++++++++--- .../modules/docforge.cli.mkdocs_utils.json | 68 +++-- docs/mcp/modules/docforge.json | 153 ++++++++--- mkdocs.yml => docs/mkdocs.lib.yml | 54 ++-- docs/mkdocs.wiki.yml | 64 +++++ docs/wiki/01_overview.md | 17 +- docs/wiki/02_architecture.md | 4 +- docs/wiki/04_iterative_workflow.md | 21 +- docs/wiki/index.md | 4 +- tests/cli/test_build_api.py | 40 +-- tests/cli/test_build_mkdocs.py | 34 +-- tests/cli/test_build_wiki.py | 37 +-- tests/cli/test_mkdocs_utils.py | 79 ++++++ 21 files changed, 942 insertions(+), 443 deletions(-) rename mkdocs.yml => docs/mkdocs.lib.yml (63%) create mode 100644 docs/mkdocs.wiki.yml create mode 100644 tests/cli/test_mkdocs_utils.py diff --git a/.gitignore b/.gitignore index e24f273..7c70e5d 100644 --- a/.gitignore +++ b/.gitignore @@ -53,6 +53,7 @@ test-results/ site/ docs/_site/ docs/build/ +mkdocs.yml # ========================= # MkDocs / Sphinx output diff --git a/README.md b/README.md index f43539d..ad56000 100644 --- a/README.md +++ b/README.md @@ -23,41 +23,49 @@ pip install doc-forge # CLI usage -## Generate an MkDocs site from a Python package: +Each site kind (`lib`, `api`, `wiki`) is built independently into `site/{kind}`. + +## Build the library reference from a Python package: ```bash doc-forge build --mkdocs --module my_package ``` +## Build the API reference from an OpenAPI spec: + +```bash +doc-forge build --api --openapi-spec spec.json +``` + +## Build the hand-written wiki: + +```bash +doc-forge build --wiki --site-name my_package +``` + ## Generate MCP JSON documentation: ```bash doc-forge build --mcp --module my_package ``` - -## Generate MkDocs site and MCP JSON documentation: +## Build several kinds in one pass: ```bash -doc-forge build --mcp --mkdocs --module my_package +doc-forge build --mcp --mkdocs --wiki --module my_package ``` -## Include a hand-written wiki in the MkDocs site: +Each enabled kind gets its own MkDocs config (`docs/mkdocs.{lib,api,wiki}.yml`) +and its own site under `site/`. + +## Serve a site locally: ```bash -doc-forge build --wiki --mkdocs --module my_package -``` - -## Build wiki pages only (no module required): - -```bash -doc-forge build --wiki --site-name my_package -``` - -## Serve MkDocs locally: - -```bash -doc-forge serve --mkdocs --module my_package +doc-forge serve --wiki # preview from docs/mkdocs.wiki.yml +doc-forge serve --lib +doc-forge serve --api +# or any config directly: +doc-forge serve --mkdocs --mkdocs-yml docs/mkdocs.wiki.yml ``` ## Serve MCP locally: diff --git a/docforge/__init__.py b/docforge/__init__.py index 09418ef..e17746a 100644 --- a/docforge/__init__.py +++ b/docforge/__init__.py @@ -22,41 +22,49 @@ pip install doc-forge # CLI usage -## Generate an MkDocs site from a Python package: +Each site kind (`lib`, `api`, `wiki`) is built independently into `site/{kind}`. + +## Build the library reference from a Python package: ```bash doc-forge build --mkdocs --module my_package ``` +## Build the API reference from an OpenAPI spec: + +```bash +doc-forge build --api --openapi-spec spec.json +``` + +## Build the hand-written wiki: + +```bash +doc-forge build --wiki --site-name my_package +``` + ## Generate MCP JSON documentation: ```bash doc-forge build --mcp --module my_package ``` - -## Generate MkDocs site and MCP JSON documentation: +## Build several kinds in one pass: ```bash -doc-forge build --mcp --mkdocs --module my_package +doc-forge build --mcp --mkdocs --wiki --module my_package ``` -## Include a hand-written wiki in the MkDocs site: +Each enabled kind gets its own MkDocs config (`docs/mkdocs.{lib,api,wiki}.yml`) +and its own site under `site/`. + +## Serve a site locally: ```bash -doc-forge build --wiki --mkdocs --module my_package -``` - -## Build wiki pages only (no module required): - -```bash -doc-forge build --wiki --site-name my_package -``` - -## Serve MkDocs locally: - -```bash -doc-forge serve --mkdocs --module my_package +doc-forge serve --wiki # preview from docs/mkdocs.wiki.yml +doc-forge serve --lib +doc-forge serve --api +# or any config directly: +doc-forge serve --mkdocs --mkdocs-yml docs/mkdocs.wiki.yml ``` ## Serve MCP locally: diff --git a/docforge/cli/commands.py b/docforge/cli/commands.py index 8d1ba46..4b19b7f 100644 --- a/docforge/cli/commands.py +++ b/docforge/cli/commands.py @@ -9,12 +9,14 @@ Provides the CLI structure using Click, including build, serve, and tree command Notes: - The `build` command validates requested modes before generating anything. - - `--mkdocs`, `--api`, and `--wiki` share a single MkDocs build; `--mcp` - generates a machine-readable bundle independently. + - `--mkdocs`, `--api`, and `--wiki` each emit their own MkDocs config and + build (`docs/mkdocs.{kind}.yml` into `site/{kind}`); `--mcp` generates a + machine-readable bundle independently. --- """ +import os from pathlib import Path import click @@ -37,10 +39,10 @@ def cli() -> None: @cli.command() @click.option("--mcp", is_flag=True, help="Build MCP resources") -@click.option("--mkdocs", is_flag=True, help="Build MkDocs site") +@click.option("--mkdocs", is_flag=True, help="Build the lib MkDocs site") @click.option("--api", is_flag=True, help="Build API docs from an OpenAPI spec") @click.option( - "--wiki", is_flag=True, help="Include a hand-written wiki in the MkDocs site" + "--wiki", is_flag=True, help="Build a hand-written wiki as its own MkDocs site" ) @click.option( "--module-is-source", @@ -54,7 +56,7 @@ def cli() -> None: help="Path to the OpenAPI JSON specification", ) @click.option("--project-name", help="Project name override") -@click.option("--site-name", help="MkDocs site name") +@click.option("--site-name", help="MkDocs site name for lib and wiki sites") @click.option( "--docs-dir", type=click.Path(path_type=Path), @@ -65,7 +67,7 @@ def cli() -> None: "--wiki-dir", type=click.Path(path_type=Path), default=Path("docs/wiki"), - help="Hand-written wiki directory included in the MkDocs site", + help="Hand-written wiki directory built as its own MkDocs site", ) @click.option( "--nav", @@ -77,12 +79,6 @@ def cli() -> None: @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), @@ -103,7 +99,6 @@ def build( wiki_dir: Path, nav_file: Path, template: Path | None, - mkdocs_yml: Path, out_dir: Path, ) -> None: """ @@ -111,33 +106,36 @@ def build( This command runs the full documentation pipeline: it loads Python modules, generates renderer-specific documentation sources, and - optionally builds or serves the final output. + optionally builds the final output. Depending on the selected options, the build can target: - - MkDocs static documentation sites for library reference docs - - Swagger-enabled API docs generated from an OpenAPI spec - - Hand-written wiki pages included in the MkDocs site - - MCP structured documentation resources + - A lib MkDocs site (`--mkdocs`) for library reference docs + - A swagger-enabled API MkDocs site (`--api`) built from an OpenAPI spec + - A wiki MkDocs site (`--wiki`) built from hand-written markdown + - MCP structured documentation resources (`--mcp`) + + Each enabled site kind produces its own MkDocs configuration + (`docs/mkdocs.{kind}.yml`) and its own build (`site/{kind}`). Notes: - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be provided. - - `--mkdocs`, `--api`, and `--wiki` are combined into a single MkDocs - build, while `--mcp` emits a machine-readable bundle. + - `--mkdocs`, `--api`, and `--wiki` emit independent MkDocs builds, + while `--mcp` emits a machine-readable bundle. Args: mcp (bool): Enable MCP documentation generation. mkdocs (bool): - Enable MkDocs library documentation generation. + Enable the lib MkDocs documentation generation. api (bool): Enable API documentation generation from an OpenAPI spec. wiki (bool): - Include a hand-written wiki directory in the MkDocs site. + Build a hand-written wiki directory as its own MkDocs site. module_is_source (bool): Treat the specified module directory as the project root. @@ -152,11 +150,10 @@ def build( Optional override for the project name. site_name (str | None): - Display name for the MkDocs site. + Display name for the lib and wiki MkDocs sites. docs_dir (Path): - Shared documentation root used as the MkDocs ``docs_dir``. - + Shared documentation root used for generated sources. wiki_dir (Path): Directory containing hand-written wiki markdown files. @@ -166,9 +163,6 @@ def build( template (Path | None): Optional custom MkDocs configuration template. - mkdocs_yml (Path): - Output path for the generated MkDocs configuration. - out_dir (Path): Output directory for generated MCP resources. @@ -188,18 +182,18 @@ def build( "the OpenAPI spec provides the site name" ) - if (mkdocs or mcp) and not module: - raise click.UsageError( - "--module is required for MkDocs build" - if mkdocs - else "--module is required for MCP build" - ) + if mkdocs and not module: + raise click.UsageError("--module is required for MkDocs build") + if mcp and not module: + raise click.UsageError("--module is required for MCP build") spec: dict | None = None if api: spec = api_utils.load_openapi_spec(openapi_spec) + kinds: list[str] = [] if mkdocs: + kinds.append("lib") lib_dir = docs_dir / "lib" click.echo(f"Generating MkDocs sources in {lib_dir}...") mkdocs_utils.generate_sources( @@ -207,54 +201,64 @@ def build( lib_dir, project_name, module_is_source, - readme_dir=mkdocs_yml.parent, + readme_dir=Path(".").resolve(), ) if api: + kinds.append("api") api_dir = docs_dir / "api" click.echo(f"Generating API sources in {api_dir}...") api_utils.generate_api_sources(spec, api_dir) - if mkdocs or api or wiki: - modes: list[str] = [] - if mkdocs: - modes.append("lib") + if wiki: + kinds.append("wiki") + + if kinds: + api_metadata: api_utils.OpenAPIMetadata | None = None if api: - modes.append("api") - if wiki: - modes.append("wiki") + api_metadata = api_utils.derive_metadata(spec) + theme_icon = mkdocs_utils.load_spec_icon(nav_file) - site_description: str | None = None - site_author: str | None = None - effective_site_name = site_name or module or Path.cwd().name + config_paths: list[Path] = [] + for kind in kinds: + kind_root = wiki_dir if kind == "wiki" else docs_dir / kind - if api: - metadata = api_utils.derive_metadata(spec) - effective_site_name = metadata.site_name - site_description = metadata.site_description - site_author = metadata.site_author + site_name_kind = site_name or module or Path.cwd().name + site_description: str | None = None + site_author: str | None = None + if kind == "api" and api_metadata: + site_name_kind = api_metadata.site_name + site_description = api_metadata.site_description + site_author = api_metadata.site_author - click.echo(f"Generating MkDocs config {mkdocs_yml}...") - mkdocs_utils.generate_config( - docs_dir, - nav_file, - template, - mkdocs_yml, - effective_site_name, - modes=modes, - site_description=site_description, - site_author=site_author, - wiki_dir=wiki_dir if wiki else None, - ) + if kind == "lib": + nav_block, _kind_icon = mkdocs_utils.build_lib_nav(nav_file, docs_dir) + elif kind == "wiki": + nav_block = mkdocs_utils.build_wiki_nav_block(wiki_dir) + else: + nav_block = [{"API Reference": "index.md"}] - click.echo("Running MkDocs build...") - mkdocs_utils.build(mkdocs_yml) - click.echo("MkDocs build completed.") + out = docs_dir / f"mkdocs.{kind}.yml" + click.echo(f"Generating MkDocs config {out}...") + mkdocs_utils.generate_site_config( + kind, + kind_root, + nav_block, + out, + site_name_kind, + Path(os.path.relpath(kind_root, docs_dir)).as_posix(), + Path(os.path.relpath(Path.cwd() / "site" / kind, docs_dir)).as_posix(), + template=template, + site_description=site_description, + site_author=site_author, + theme_icon=theme_icon, + ) + config_paths.append(out) + + click.echo("Running MkDocs builds...") + mkdocs_utils.build_configs(config_paths) 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.") @@ -262,12 +266,15 @@ def build( @cli.command() @click.option("--mcp", is_flag=True, help="Serve MCP documentation") -@click.option("--mkdocs", is_flag=True, help="Serve MkDocs site") +@click.option("--mkdocs", is_flag=True, help="Serve an MkDocs site from --mkdocs-yml") +@click.option("--lib", is_flag=True, help="Serve the lib MkDocs site") +@click.option("--api", is_flag=True, help="Serve the API MkDocs site") +@click.option("--wiki", is_flag=True, help="Serve the wiki MkDocs site") @click.option("--module", help="Python module to serve") @click.option( "--mkdocs-yml", type=click.Path(path_type=Path), - default=Path("mkdocs.yml"), + default=Path("docs/mkdocs.wiki.yml"), help="MkDocs config path", ) @click.option( @@ -279,6 +286,9 @@ def build( def serve( mcp: bool, mkdocs: bool, + lib: bool, + api: bool, + wiki: bool, module: str | None, mkdocs_yml: Path, out_dir: Path, @@ -288,15 +298,28 @@ def serve( Depending on the selected mode, this command starts either: - - A MkDocs development server for browsing documentation + - A MkDocs development server for browsing a site, or - An MCP server exposing structured documentation resources + The kind flags (`--lib`, `--api`, `--wiki`) select the generated + per-kind config (`docs/mkdocs.{kind}.yml`); `--mkdocs` serves the config + passed via `--mkdocs-yml`. + Args: mcp (bool): Serve documentation using the MCP server. mkdocs (bool): - Serve the MkDocs development site. + Serve the MkDocs development site from ``--mkdocs-yml``. + + lib (bool): + Serve the lib MkDocs site. + + api (bool): + Serve the API MkDocs site. + + wiki (bool): + Serve the wiki MkDocs site. module (str | None): Python module import path to serve via MCP. @@ -311,17 +334,37 @@ def serve( 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") + selected = [ + name + for name, enabled in ( + ("mcp", mcp), + ("mkdocs", mkdocs), + ("lib", lib), + ("api", api), + ("wiki", wiki), + ) + if enabled + ] + if len(selected) != 1: + raise click.UsageError( + "Must specify exactly one of --mcp, --mkdocs, --lib, --api, --wiki" + ) - if mkdocs: - mkdocs_utils.serve(mkdocs_yml) - elif mcp: + if mcp: + if not module: + raise click.UsageError("--module is required for MCP serve") mcp_utils.serve(module, out_dir) + return + + config = mkdocs_yml + if lib: + config = Path("docs/mkdocs.lib.yml") + elif api: + config = Path("docs/mkdocs.api.yml") + elif wiki: + config = Path("docs/mkdocs.wiki.yml") + + mkdocs_utils.serve(config) @cli.command() diff --git a/docforge/cli/commands.pyi b/docforge/cli/commands.pyi index 4b2bd22..0b4cb69 100644 --- a/docforge/cli/commands.pyi +++ b/docforge/cli/commands.pyi @@ -20,12 +20,14 @@ def build( wiki_dir: Path, nav_file: Path, template: Path | None, - mkdocs_yml: Path, out_dir: Path, ) -> None: ... def serve( mcp: bool, mkdocs: bool, + lib: bool, + api: bool, + wiki: bool, module: str | None, mkdocs_yml: Path, out_dir: Path, diff --git a/docforge/cli/mkdocs_utils.py b/docforge/cli/mkdocs_utils.py index db9b002..fb349f4 100644 --- a/docforge/cli/mkdocs_utils.py +++ b/docforge/cli/mkdocs_utils.py @@ -6,17 +6,19 @@ Utilities for working with MkDocs in the doc-forge CLI. --- Notes: - - A single generated `mkdocs.yml` serves lib, api, and wiki content with - merged navigation. Wiki navigation, when enabled, precedes every other - group and its `index.md` becomes the site `Home`. + - A separate `mkdocs.{kind}.yml` configuration and build is emitted per + enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and + written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`). + - Navigation blocks are re-rooted per kind: the wiki navigation drops its + leading `wiki/` scope and the resolved nav spec drops its `lib/` scope. --- """ -import os from collections.abc import Iterable from importlib import resources from pathlib import Path +from typing import Any, cast import click import yaml @@ -83,53 +85,171 @@ def generate_sources( ) -def generate_config( - docs_dir: Path, +def build_lib_nav( nav_file: Path, - template: Path | None, - out: Path, - site_name: str, - modes: Iterable[str] | None = None, - site_description: str | None = None, - site_author: str | None = None, - wiki_dir: Path | None = None, -) -> None: + docs_root: Path, +) -> tuple[list[dict[str, Any]], dict[str, str] | None]: """ - Generate an `mkdocs.yml` configuration file. + Build the re-rooted navigation block for a lib site. - The configuration is created by combining a template configuration - with a navigation structure derived from the docforge navigation - specification (and, when a wiki directory is provided, from the wiki - file structure). - - The ``docs_dir`` is always written relative to the MkDocs root and is - expected to be the shared documentation parent (for example ``docs``), - with generated sources nested under ``lib/`` or ``api/`` subdirectories - and hand-written wiki content under a ``wiki/`` subdirectory. + The navigation specification is resolved against the shared documentation + root and every resulting path is re-rooted relative to the ``lib`` + subdirectory by stripping its leading ``lib/`` scope component. Args: - docs_dir (Path): - Shared documentation root used as the MkDocs ``docs_dir``. - nav_file (Path): Path to the `docforge.nav.yml` navigation specification. - template (Path | None): - Optional path to a fully custom MkDocs configuration template. - If not provided, built-in templates are merged; the provided - template replaces the built-in templates entirely. + docs_root (Path): + Shared documentation root containing the ``lib`` sources. + + Returns: + tuple[list[dict[str, Any]], dict[str, str] | None]: + The re-rooted navigation block and the optional theme icon + mapping from the specification. + + Raises: + click.FileError: + If the navigation specification cannot be found. + """ + if not nav_file.exists(): + raise click.FileError(str(nav_file), hint="Nav spec not found") + + spec = load_nav_spec(nav_file) + resolved = resolve_nav(spec, docs_root) + block = MkDocsNavEmitter().emit(resolved) + return cast(list[dict[str, Any]], _strip_scope(block, "lib")), spec.icon + + +def build_wiki_nav_block(wiki_dir: Path) -> list[dict[str, Any]]: + """ + Build the re-rooted navigation block for a wiki site. + + The wiki navigation derived from the wiki file structure is re-rooted + relative to the wiki directory itself by stripping the leading ``wiki/`` + scope component. + + Args: + wiki_dir (Path): + Path to the hand-written wiki directory, for example ``docs/wiki``. + + Returns: + list[dict[str, Any]]: + Navigation entries relative to the wiki directory. + + Raises: + click.FileError: + If the wiki directory does not exist. + """ + if not wiki_dir.exists(): + raise click.FileError(str(wiki_dir), hint="Wiki dir not found") + + return cast(list[dict[str, Any]], _strip_scope(build_wiki_nav(wiki_dir), "wiki")) + + +def load_spec_icon(nav_file: Path) -> dict[str, str] | None: + """ + Load the theme icon mapping from a navigation specification. + + Args: + nav_file (Path): + Path to the navigation specification file. + + Returns: + dict[str, str] | None: + The icon mapping, or ``None`` when the specification file is + absent or cannot be parsed. + """ + if not nav_file.exists(): + return None + + try: + return load_nav_spec(nav_file).icon + except (ValueError, yaml.YAMLError): + return None + + +def _strip_scope(value: object, scope: str) -> object: + """ + Re-root navigation paths by removing a leading scope component. + + The navigation block is walked recursively and every relative path + string that starts with ``{scope}/`` has that prefix removed, so a lib + or wiki site scoped to its own ``docs_dir`` can reuse paths that were + originally written relative to the shared documentation root. + + Args: + value (object): + Navigation block, group list, path string, or scalar value. + scope (str): + Leading path component to strip, for example ``lib`` or ``wiki``. + + Returns: + object: + The navigation structure with re-rooted paths. + """ + if isinstance(value, str): + prefix = f"{scope}/" + return value[len(prefix) :] if value.startswith(prefix) else value + if isinstance(value, list): + return [_strip_scope(item, scope) for item in value] + if isinstance(value, dict): + return {key: _strip_scope(val, scope) for key, val in value.items()} + return value + + +def generate_site_config( + kind: str, + kind_root: Path, + nav_block: list[dict[str, Any]], + out: Path, + site_name: str, + docs_dir: str, + site_dir: str, + template: Path | None = None, + site_description: str | None = None, + site_author: str | None = None, + theme_icon: dict[str, str] | None = None, +) -> None: + """ + Generate a per-kind `mkdocs.{kind}.yml` configuration file. + + The configuration is created by merging the shared ``mkdocs.common.yml`` + template with the fragment contributed by the kind (``lib``, ``api``, or + ``wiki``). Both ``docs_dir`` and ``site_dir`` are written relative to the + configuration file's directory: the kind's sources when expressed as a + sibling path (for example ``lib``) and the per-kind site output (for + example ``../site/lib``). + + Args: + kind (str): + Documentation kind, one of ``lib``, ``api``, or ``wiki``. + + kind_root (Path): + Directory scoped to the kind (for example ``docs/lib``) that + serves as the MkDocs ``docs_dir``. + + nav_block (list[dict[str, Any]]): + Re-rooted navigation entries for the kind's site. out (Path): - Destination path where the generated `mkdocs.yml` file will be written. + Destination path where the generated ``mkdocs.{kind}.yml`` file + is written. site_name (str): Display name for the generated documentation site. - modes (Iterable[str] | None): - Documentation modes to enable. Each mode contributes its own - built-in template fragment (for example ``lib``, ``api``, or - ``wiki``), merged on top of the shared ``mkdocs.common.yml`` - template. + docs_dir (str): + MkDocs ``docs_dir`` value, relative to the configuration + file's directory. + + site_dir (str): + MkDocs ``site_dir`` value, relative to the configuration + file's directory. + + template (Path | None): + Optional path to a fully custom MkDocs configuration template + that replaces the built-in templates entirely. site_description (str | None): Optional site description written into the configuration. @@ -137,50 +257,26 @@ def generate_config( site_author (str | None): Optional site author written into the configuration. - wiki_dir (Path | None): - Optional path to a hand-written wiki directory (for example - ``docs/wiki``). When provided, the site navigation is derived - from the wiki file structure and placed before the navigation - groups defined in ``nav_file``. - - Raises: - click.FileError: - If the navigation specification, template, or wiki directory - cannot be found. + theme_icon (dict[str, str] | None): + Optional mapping of theme icon entries injected as + ``theme.icon``. """ - if not nav_file.exists() and wiki_dir is None: - raise click.FileError(str(nav_file), hint="Nav spec not found") - - nav_block: list[dict] = [] - if nav_file.exists(): - spec = load_nav_spec(nav_file) - resolved = resolve_nav(spec, docs_dir) - nav_block = MkDocsNavEmitter().emit(resolved) - else: - spec = None - - if wiki_dir is not None: - if not wiki_dir.exists(): - raise click.FileError(str(wiki_dir), hint="Wiki dir not found") - wiki_nav = build_wiki_nav(wiki_dir) - if wiki_nav: - nav_block = wiki_nav + [entry for entry in nav_block if "Home" not in entry] - - data = _load_template(template, modes) + data = _load_template(template, [kind]) data["site_name"] = site_name if site_description: data["site_description"] = site_description if site_author: data["site_author"] = site_author - data["docs_dir"] = Path(os.path.relpath(docs_dir, out.parent)).as_posix() + data["docs_dir"] = docs_dir + data["site_dir"] = site_dir data["nav"] = nav_block - if spec is not None and spec.icon: + if theme_icon: theme = data.setdefault("theme", {}) if not isinstance(theme, dict): theme = {} - theme["icon"] = spec.icon + theme["icon"] = theme_icon data["theme"] = theme out.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8") @@ -310,28 +406,28 @@ def _deep_merge(base: dict, part: dict) -> dict: return base -def build(mkdocs_yml: Path) -> None: +def build_configs(yml_paths: Iterable[Path]) -> None: """ - Build the MkDocs documentation site. + Build the MkDocs documentation site for every given configuration. - This function loads the MkDocs configuration and runs the MkDocs - build command to generate the final static documentation site. + Each configuration file is loaded and built in turn, producing the + per-kind static sites (``site/lib``, ``site/api``, ``site/wiki``). Args: - mkdocs_yml (Path): - Path to the `mkdocs.yml` configuration file. + yml_paths (Iterable[Path]): + Configuration files to build, in order. Raises: click.ClickException: - If the configuration file does not exist. + If a configuration file does not exist. """ - if not mkdocs_yml.exists(): - raise click.ClickException(f"mkdocs.yml not found: {mkdocs_yml}") - from mkdocs.commands.build import build as mkdocs_build from mkdocs.config import load_config - mkdocs_build(load_config(str(mkdocs_yml))) + for yml in yml_paths: + if not yml.exists(): + raise click.ClickException(f"mkdocs.yml not found: {yml}") + mkdocs_build(load_config(str(yml))) def serve(mkdocs_yml: Path) -> None: diff --git a/docforge/cli/mkdocs_utils.pyi b/docforge/cli/mkdocs_utils.pyi index 988a857..776ace6 100644 --- a/docforge/cli/mkdocs_utils.pyi +++ b/docforge/cli/mkdocs_utils.pyi @@ -1,5 +1,6 @@ from collections.abc import Iterable from pathlib import Path +from typing import Any def generate_sources( module: str, @@ -8,16 +9,25 @@ def generate_sources( module_is_source: bool | None = None, readme_dir: Path | None = None, ) -> None: ... -def generate_config( - docs_dir: Path, +def build_lib_nav( nav_file: Path, - template: Path | None, + docs_root: Path, +) -> tuple[list[dict[str, Any]], dict[str, str] | None]: ... +def build_wiki_nav_block(wiki_dir: Path) -> list[dict[str, Any]]: ... +def load_spec_icon(nav_file: Path) -> dict[str, str] | None: ... +def _strip_scope(value: object, scope: str) -> object: ... +def generate_site_config( + kind: str, + kind_root: Path, + nav_block: list[dict[str, Any]], out: Path, site_name: str, - modes: Iterable[str] | None = None, + docs_dir: str, + site_dir: str, + template: Path | None = None, site_description: str | None = None, site_author: str | None = None, - wiki_dir: Path | None = None, + theme_icon: dict[str, str] | None = None, ) -> None: ... -def build(mkdocs_yml: Path) -> None: ... +def build_configs(yml_paths: Iterable[Path]) -> None: ... def serve(mkdocs_yml: Path) -> None: ... diff --git a/docs/mcp/modules/docforge.cli.commands.json b/docs/mcp/modules/docforge.cli.commands.json index 9dfa53e..dcec1a1 100644 --- a/docs/mcp/modules/docforge.cli.commands.json +++ b/docs/mcp/modules/docforge.cli.commands.json @@ -2,8 +2,15 @@ "module": "docforge.cli.commands", "content": { "path": "docforge.cli.commands", - "docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` share a single MkDocs build; `--mcp`\n generates a machine-readable bundle independently.\n\n---", + "docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` each emit their own MkDocs config and\n build (`docs/mkdocs.{kind}.yml` into `site/{kind}`); `--mcp` generates a\n machine-readable bundle independently.\n\n---", "objects": { + "os": { + "name": "os", + "kind": "alias", + "path": "docforge.cli.commands.os", + "signature": "", + "docstring": null + }, "Path": { "name": "Path", "kind": "alias", @@ -245,15 +252,8 @@ "kind": "module", "path": "docforge.cli.commands.mkdocs_utils", "signature": "", - "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A single generated `mkdocs.yml` serves lib, api, and wiki content with\n merged navigation. Wiki navigation, when enabled, precedes every other\n group and its `index.md` becomes the site `Home`.\n\n---", + "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A separate `mkdocs.{kind}.yml` configuration and build is emitted per\n enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and\n written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).\n - Navigation blocks are re-rooted per kind: the wiki navigation drops its\n leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.\n\n---", "members": { - "os": { - "name": "os", - "kind": "alias", - "path": "docforge.cli.commands.mkdocs_utils.os", - "signature": "", - "docstring": null - }, "Iterable": { "name": "Iterable", "kind": "alias", @@ -275,6 +275,20 @@ "signature": "", "docstring": null }, + "Any": { + "name": "Any", + "kind": "alias", + "path": "docforge.cli.commands.mkdocs_utils.Any", + "signature": "", + "docstring": null + }, + "cast": { + "name": "cast", + "kind": "alias", + "path": "docforge.cli.commands.mkdocs_utils.cast", + "signature": "", + "docstring": null + }, "click": { "name": "click", "kind": "alias", @@ -393,19 +407,40 @@ "signature": "", "docstring": "Generate MkDocs Markdown sources for a Python module.\n\nThis function introspects the specified module, builds the internal\ndocumentation model, and renders Markdown documentation files for\nuse with MkDocs.\n\nArgs:\n module (str):\n Python module import path used as the entry point for\n documentation generation.\n\n docs_dir (Path):\n Directory where the generated Markdown files will be written.\n\n project_name (str | None):\n Optional override for the project name used in documentation metadata.\n\n module_is_source (bool | None):\n If True, treat the specified module directory as the project root\n rather than a nested module.\n\n readme_dir (Path | None):\n Directory where the generated README.md should be written. If not\n provided, defaults to the parent of ``docs_dir``." }, - "generate_config": { - "name": "generate_config", + "build_lib_nav": { + "name": "build_lib_nav", "kind": "function", - "path": "docforge.cli.commands.mkdocs_utils.generate_config", - "signature": "", - "docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found." + "path": "docforge.cli.commands.mkdocs_utils.build_lib_nav", + "signature": "", + "docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found." }, - "build": { - "name": "build", + "build_wiki_nav_block": { + "name": "build_wiki_nav_block", "kind": "function", - "path": "docforge.cli.commands.mkdocs_utils.build", - "signature": "", - "docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." + "path": "docforge.cli.commands.mkdocs_utils.build_wiki_nav_block", + "signature": "", + "docstring": "Build the re-rooted navigation block for a wiki site.\n\nThe wiki navigation derived from the wiki file structure is re-rooted\nrelative to the wiki directory itself by stripping the leading ``wiki/``\nscope component.\n\nArgs:\n wiki_dir (Path):\n Path to the hand-written wiki directory, for example ``docs/wiki``.\n\nReturns:\n list[dict[str, Any]]:\n Navigation entries relative to the wiki directory.\n\nRaises:\n click.FileError:\n If the wiki directory does not exist." + }, + "load_spec_icon": { + "name": "load_spec_icon", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.load_spec_icon", + "signature": "", + "docstring": "Load the theme icon mapping from a navigation specification.\n\nArgs:\n nav_file (Path):\n Path to the navigation specification file.\n\nReturns:\n dict[str, str] | None:\n The icon mapping, or ``None`` when the specification file is\n absent or cannot be parsed." + }, + "generate_site_config": { + "name": "generate_site_config", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.generate_site_config", + "signature": "", + "docstring": "Generate a per-kind `mkdocs.{kind}.yml` configuration file.\n\nThe configuration is created by merging the shared ``mkdocs.common.yml``\ntemplate with the fragment contributed by the kind (``lib``, ``api``, or\n``wiki``). Both ``docs_dir`` and ``site_dir`` are written relative to the\nconfiguration file's directory: the kind's sources when expressed as a\nsibling path (for example ``lib``) and the per-kind site output (for\nexample ``../site/lib``).\n\nArgs:\n kind (str):\n Documentation kind, one of ``lib``, ``api``, or ``wiki``.\n\n kind_root (Path):\n Directory scoped to the kind (for example ``docs/lib``) that\n serves as the MkDocs ``docs_dir``.\n\n nav_block (list[dict[str, Any]]):\n Re-rooted navigation entries for the kind's site.\n\n out (Path):\n Destination path where the generated ``mkdocs.{kind}.yml`` file\n is written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n docs_dir (str):\n MkDocs ``docs_dir`` value, relative to the configuration\n file's directory.\n\n site_dir (str):\n MkDocs ``site_dir`` value, relative to the configuration\n file's directory.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template\n that replaces the built-in templates entirely.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n theme_icon (dict[str, str] | None):\n Optional mapping of theme icon entries injected as\n ``theme.icon``." + }, + "build_configs": { + "name": "build_configs", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.build_configs", + "signature": "", + "docstring": "Build the MkDocs documentation site for every given configuration.\n\nEach configuration file is loaded and built in turn, producing the\nper-kind static sites (``site/lib``, ``site/api``, ``site/wiki``).\n\nArgs:\n yml_paths (Iterable[Path]):\n Configuration files to build, in order.\n\nRaises:\n click.ClickException:\n If a configuration file does not exist." }, "serve": { "name": "serve", @@ -522,21 +557,21 @@ "name": "build", "kind": "function", "path": "docforge.cli.commands.build", - "signature": "", - "docstring": "Build documentation artifacts.\n\nThis command runs the full documentation pipeline: it loads Python\nmodules, generates renderer-specific documentation sources, and\noptionally builds or serves the final output.\n\nDepending on the selected options, the build can target:\n\n- MkDocs static documentation sites for library reference docs\n- Swagger-enabled API docs generated from an OpenAPI spec\n- Hand-written wiki pages included in the MkDocs site\n- MCP structured documentation resources\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` are combined into a single MkDocs\n build, while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable MkDocs library documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Include a hand-written wiki directory in the MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the MkDocs site.\n\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n mkdocs_yml (Path):\n Output path for the generated MkDocs configuration.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting." + "signature": "", + "docstring": "Build documentation artifacts.\n\nThis command runs the full documentation pipeline: it loads Python\nmodules, generates renderer-specific documentation sources, and\noptionally builds the final output.\n\nDepending on the selected options, the build can target:\n\n- A lib MkDocs site (`--mkdocs`) for library reference docs\n- A swagger-enabled API MkDocs site (`--api`) built from an OpenAPI spec\n- A wiki MkDocs site (`--wiki`) built from hand-written markdown\n- MCP structured documentation resources (`--mcp`)\n\nEach enabled site kind produces its own MkDocs configuration\n(`docs/mkdocs.{kind}.yml`) and its own build (`site/{kind}`).\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` emit independent MkDocs builds,\n while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable the lib MkDocs documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Build a hand-written wiki directory as its own MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the lib and wiki MkDocs sites.\n\n docs_dir (Path):\n Shared documentation root used for generated sources.\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting." }, "serve": { "name": "serve", "kind": "function", "path": "docforge.cli.commands.serve", - "signature": "", - "docstring": "Serve generated documentation locally.\n\nDepending on the selected mode, this command starts either:\n\n- A MkDocs development server for browsing documentation\n- An MCP server exposing structured documentation resources\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided." + "signature": "", + "docstring": "Serve generated documentation locally.\n\nDepending on the selected mode, this command starts either:\n\n- A MkDocs development server for browsing a site, or\n- An MCP server exposing structured documentation resources\n\nThe kind flags (`--lib`, `--api`, `--wiki`) select the generated\nper-kind config (`docs/mkdocs.{kind}.yml`); `--mkdocs` serves the config\npassed via `--mkdocs-yml`.\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site from ``--mkdocs-yml``.\n\n lib (bool):\n Serve the lib MkDocs site.\n\n api (bool):\n Serve the API MkDocs site.\n\n wiki (bool):\n Serve the wiki MkDocs site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided." }, "tree": { "name": "tree", "kind": "function", "path": "docforge.cli.commands.tree", - "signature": "", + "signature": "", "docstring": "Display the documentation object tree for a module.\n\nThis command introspects the specified module and prints a\nhierarchical representation of the discovered documentation\nobjects, including modules, classes, functions, and members.\n\nArgs:\n module (str):\n Python module import path to introspect.\n\n project_name (str | None):\n Optional name to display as the project root." }, "Group": { diff --git a/docs/mcp/modules/docforge.cli.json b/docs/mcp/modules/docforge.cli.json index 92d61f5..75cc4e9 100644 --- a/docs/mcp/modules/docforge.cli.json +++ b/docs/mcp/modules/docforge.cli.json @@ -134,8 +134,15 @@ "kind": "module", "path": "docforge.cli.commands", "signature": null, - "docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` share a single MkDocs build; `--mcp`\n generates a machine-readable bundle independently.\n\n---", + "docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` each emit their own MkDocs config and\n build (`docs/mkdocs.{kind}.yml` into `site/{kind}`); `--mcp` generates a\n machine-readable bundle independently.\n\n---", "members": { + "os": { + "name": "os", + "kind": "alias", + "path": "docforge.cli.commands.os", + "signature": "", + "docstring": null + }, "Path": { "name": "Path", "kind": "alias", @@ -377,15 +384,8 @@ "kind": "module", "path": "docforge.cli.commands.mkdocs_utils", "signature": "", - "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A single generated `mkdocs.yml` serves lib, api, and wiki content with\n merged navigation. Wiki navigation, when enabled, precedes every other\n group and its `index.md` becomes the site `Home`.\n\n---", + "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A separate `mkdocs.{kind}.yml` configuration and build is emitted per\n enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and\n written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).\n - Navigation blocks are re-rooted per kind: the wiki navigation drops its\n leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.\n\n---", "members": { - "os": { - "name": "os", - "kind": "alias", - "path": "docforge.cli.commands.mkdocs_utils.os", - "signature": "", - "docstring": null - }, "Iterable": { "name": "Iterable", "kind": "alias", @@ -407,6 +407,20 @@ "signature": "", "docstring": null }, + "Any": { + "name": "Any", + "kind": "alias", + "path": "docforge.cli.commands.mkdocs_utils.Any", + "signature": "", + "docstring": null + }, + "cast": { + "name": "cast", + "kind": "alias", + "path": "docforge.cli.commands.mkdocs_utils.cast", + "signature": "", + "docstring": null + }, "click": { "name": "click", "kind": "alias", @@ -525,19 +539,40 @@ "signature": "", "docstring": "Generate MkDocs Markdown sources for a Python module.\n\nThis function introspects the specified module, builds the internal\ndocumentation model, and renders Markdown documentation files for\nuse with MkDocs.\n\nArgs:\n module (str):\n Python module import path used as the entry point for\n documentation generation.\n\n docs_dir (Path):\n Directory where the generated Markdown files will be written.\n\n project_name (str | None):\n Optional override for the project name used in documentation metadata.\n\n module_is_source (bool | None):\n If True, treat the specified module directory as the project root\n rather than a nested module.\n\n readme_dir (Path | None):\n Directory where the generated README.md should be written. If not\n provided, defaults to the parent of ``docs_dir``." }, - "generate_config": { - "name": "generate_config", + "build_lib_nav": { + "name": "build_lib_nav", "kind": "function", - "path": "docforge.cli.commands.mkdocs_utils.generate_config", - "signature": "", - "docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found." + "path": "docforge.cli.commands.mkdocs_utils.build_lib_nav", + "signature": "", + "docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found." }, - "build": { - "name": "build", + "build_wiki_nav_block": { + "name": "build_wiki_nav_block", "kind": "function", - "path": "docforge.cli.commands.mkdocs_utils.build", - "signature": "", - "docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." + "path": "docforge.cli.commands.mkdocs_utils.build_wiki_nav_block", + "signature": "", + "docstring": "Build the re-rooted navigation block for a wiki site.\n\nThe wiki navigation derived from the wiki file structure is re-rooted\nrelative to the wiki directory itself by stripping the leading ``wiki/``\nscope component.\n\nArgs:\n wiki_dir (Path):\n Path to the hand-written wiki directory, for example ``docs/wiki``.\n\nReturns:\n list[dict[str, Any]]:\n Navigation entries relative to the wiki directory.\n\nRaises:\n click.FileError:\n If the wiki directory does not exist." + }, + "load_spec_icon": { + "name": "load_spec_icon", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.load_spec_icon", + "signature": "", + "docstring": "Load the theme icon mapping from a navigation specification.\n\nArgs:\n nav_file (Path):\n Path to the navigation specification file.\n\nReturns:\n dict[str, str] | None:\n The icon mapping, or ``None`` when the specification file is\n absent or cannot be parsed." + }, + "generate_site_config": { + "name": "generate_site_config", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.generate_site_config", + "signature": "", + "docstring": "Generate a per-kind `mkdocs.{kind}.yml` configuration file.\n\nThe configuration is created by merging the shared ``mkdocs.common.yml``\ntemplate with the fragment contributed by the kind (``lib``, ``api``, or\n``wiki``). Both ``docs_dir`` and ``site_dir`` are written relative to the\nconfiguration file's directory: the kind's sources when expressed as a\nsibling path (for example ``lib``) and the per-kind site output (for\nexample ``../site/lib``).\n\nArgs:\n kind (str):\n Documentation kind, one of ``lib``, ``api``, or ``wiki``.\n\n kind_root (Path):\n Directory scoped to the kind (for example ``docs/lib``) that\n serves as the MkDocs ``docs_dir``.\n\n nav_block (list[dict[str, Any]]):\n Re-rooted navigation entries for the kind's site.\n\n out (Path):\n Destination path where the generated ``mkdocs.{kind}.yml`` file\n is written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n docs_dir (str):\n MkDocs ``docs_dir`` value, relative to the configuration\n file's directory.\n\n site_dir (str):\n MkDocs ``site_dir`` value, relative to the configuration\n file's directory.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template\n that replaces the built-in templates entirely.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n theme_icon (dict[str, str] | None):\n Optional mapping of theme icon entries injected as\n ``theme.icon``." + }, + "build_configs": { + "name": "build_configs", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.build_configs", + "signature": "", + "docstring": "Build the MkDocs documentation site for every given configuration.\n\nEach configuration file is loaded and built in turn, producing the\nper-kind static sites (``site/lib``, ``site/api``, ``site/wiki``).\n\nArgs:\n yml_paths (Iterable[Path]):\n Configuration files to build, in order.\n\nRaises:\n click.ClickException:\n If a configuration file does not exist." }, "serve": { "name": "serve", @@ -654,21 +689,21 @@ "name": "build", "kind": "function", "path": "docforge.cli.commands.build", - "signature": "", - "docstring": "Build documentation artifacts.\n\nThis command runs the full documentation pipeline: it loads Python\nmodules, generates renderer-specific documentation sources, and\noptionally builds or serves the final output.\n\nDepending on the selected options, the build can target:\n\n- MkDocs static documentation sites for library reference docs\n- Swagger-enabled API docs generated from an OpenAPI spec\n- Hand-written wiki pages included in the MkDocs site\n- MCP structured documentation resources\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` are combined into a single MkDocs\n build, while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable MkDocs library documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Include a hand-written wiki directory in the MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the MkDocs site.\n\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n mkdocs_yml (Path):\n Output path for the generated MkDocs configuration.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting." + "signature": "", + "docstring": "Build documentation artifacts.\n\nThis command runs the full documentation pipeline: it loads Python\nmodules, generates renderer-specific documentation sources, and\noptionally builds the final output.\n\nDepending on the selected options, the build can target:\n\n- A lib MkDocs site (`--mkdocs`) for library reference docs\n- A swagger-enabled API MkDocs site (`--api`) built from an OpenAPI spec\n- A wiki MkDocs site (`--wiki`) built from hand-written markdown\n- MCP structured documentation resources (`--mcp`)\n\nEach enabled site kind produces its own MkDocs configuration\n(`docs/mkdocs.{kind}.yml`) and its own build (`site/{kind}`).\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` emit independent MkDocs builds,\n while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable the lib MkDocs documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Build a hand-written wiki directory as its own MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the lib and wiki MkDocs sites.\n\n docs_dir (Path):\n Shared documentation root used for generated sources.\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting." }, "serve": { "name": "serve", "kind": "function", "path": "docforge.cli.commands.serve", - "signature": "", - "docstring": "Serve generated documentation locally.\n\nDepending on the selected mode, this command starts either:\n\n- A MkDocs development server for browsing documentation\n- An MCP server exposing structured documentation resources\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided." + "signature": "", + "docstring": "Serve generated documentation locally.\n\nDepending on the selected mode, this command starts either:\n\n- A MkDocs development server for browsing a site, or\n- An MCP server exposing structured documentation resources\n\nThe kind flags (`--lib`, `--api`, `--wiki`) select the generated\nper-kind config (`docs/mkdocs.{kind}.yml`); `--mkdocs` serves the config\npassed via `--mkdocs-yml`.\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site from ``--mkdocs-yml``.\n\n lib (bool):\n Serve the lib MkDocs site.\n\n api (bool):\n Serve the API MkDocs site.\n\n wiki (bool):\n Serve the wiki MkDocs site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided." }, "tree": { "name": "tree", "kind": "function", "path": "docforge.cli.commands.tree", - "signature": "", + "signature": "", "docstring": "Display the documentation object tree for a module.\n\nThis command introspects the specified module and prints a\nhierarchical representation of the discovered documentation\nobjects, including modules, classes, functions, and members.\n\nArgs:\n module (str):\n Python module import path to introspect.\n\n project_name (str | None):\n Optional name to display as the project root." }, "Group": { @@ -805,15 +840,8 @@ "kind": "module", "path": "docforge.cli.mkdocs_utils", "signature": null, - "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A single generated `mkdocs.yml` serves lib, api, and wiki content with\n merged navigation. Wiki navigation, when enabled, precedes every other\n group and its `index.md` becomes the site `Home`.\n\n---", + "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A separate `mkdocs.{kind}.yml` configuration and build is emitted per\n enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and\n written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).\n - Navigation blocks are re-rooted per kind: the wiki navigation drops its\n leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.\n\n---", "members": { - "os": { - "name": "os", - "kind": "alias", - "path": "docforge.cli.mkdocs_utils.os", - "signature": "", - "docstring": null - }, "Iterable": { "name": "Iterable", "kind": "alias", @@ -835,6 +863,20 @@ "signature": "", "docstring": null }, + "Any": { + "name": "Any", + "kind": "alias", + "path": "docforge.cli.mkdocs_utils.Any", + "signature": "", + "docstring": null + }, + "cast": { + "name": "cast", + "kind": "alias", + "path": "docforge.cli.mkdocs_utils.cast", + "signature": "", + "docstring": null + }, "click": { "name": "click", "kind": "alias", @@ -950,28 +992,49 @@ "name": "generate_sources", "kind": "function", "path": "docforge.cli.mkdocs_utils.generate_sources", - "signature": "", + "signature": "", "docstring": "Generate MkDocs Markdown sources for a Python module.\n\nThis function introspects the specified module, builds the internal\ndocumentation model, and renders Markdown documentation files for\nuse with MkDocs.\n\nArgs:\n module (str):\n Python module import path used as the entry point for\n documentation generation.\n\n docs_dir (Path):\n Directory where the generated Markdown files will be written.\n\n project_name (str | None):\n Optional override for the project name used in documentation metadata.\n\n module_is_source (bool | None):\n If True, treat the specified module directory as the project root\n rather than a nested module.\n\n readme_dir (Path | None):\n Directory where the generated README.md should be written. If not\n provided, defaults to the parent of ``docs_dir``." }, - "generate_config": { - "name": "generate_config", + "build_lib_nav": { + "name": "build_lib_nav", "kind": "function", - "path": "docforge.cli.mkdocs_utils.generate_config", - "signature": "", - "docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found." + "path": "docforge.cli.mkdocs_utils.build_lib_nav", + "signature": "", + "docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found." }, - "build": { - "name": "build", + "build_wiki_nav_block": { + "name": "build_wiki_nav_block", "kind": "function", - "path": "docforge.cli.mkdocs_utils.build", - "signature": "", - "docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." + "path": "docforge.cli.mkdocs_utils.build_wiki_nav_block", + "signature": "", + "docstring": "Build the re-rooted navigation block for a wiki site.\n\nThe wiki navigation derived from the wiki file structure is re-rooted\nrelative to the wiki directory itself by stripping the leading ``wiki/``\nscope component.\n\nArgs:\n wiki_dir (Path):\n Path to the hand-written wiki directory, for example ``docs/wiki``.\n\nReturns:\n list[dict[str, Any]]:\n Navigation entries relative to the wiki directory.\n\nRaises:\n click.FileError:\n If the wiki directory does not exist." + }, + "load_spec_icon": { + "name": "load_spec_icon", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.load_spec_icon", + "signature": "", + "docstring": "Load the theme icon mapping from a navigation specification.\n\nArgs:\n nav_file (Path):\n Path to the navigation specification file.\n\nReturns:\n dict[str, str] | None:\n The icon mapping, or ``None`` when the specification file is\n absent or cannot be parsed." + }, + "generate_site_config": { + "name": "generate_site_config", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.generate_site_config", + "signature": "", + "docstring": "Generate a per-kind `mkdocs.{kind}.yml` configuration file.\n\nThe configuration is created by merging the shared ``mkdocs.common.yml``\ntemplate with the fragment contributed by the kind (``lib``, ``api``, or\n``wiki``). Both ``docs_dir`` and ``site_dir`` are written relative to the\nconfiguration file's directory: the kind's sources when expressed as a\nsibling path (for example ``lib``) and the per-kind site output (for\nexample ``../site/lib``).\n\nArgs:\n kind (str):\n Documentation kind, one of ``lib``, ``api``, or ``wiki``.\n\n kind_root (Path):\n Directory scoped to the kind (for example ``docs/lib``) that\n serves as the MkDocs ``docs_dir``.\n\n nav_block (list[dict[str, Any]]):\n Re-rooted navigation entries for the kind's site.\n\n out (Path):\n Destination path where the generated ``mkdocs.{kind}.yml`` file\n is written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n docs_dir (str):\n MkDocs ``docs_dir`` value, relative to the configuration\n file's directory.\n\n site_dir (str):\n MkDocs ``site_dir`` value, relative to the configuration\n file's directory.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template\n that replaces the built-in templates entirely.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n theme_icon (dict[str, str] | None):\n Optional mapping of theme icon entries injected as\n ``theme.icon``." + }, + "build_configs": { + "name": "build_configs", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.build_configs", + "signature": "", + "docstring": "Build the MkDocs documentation site for every given configuration.\n\nEach configuration file is loaded and built in turn, producing the\nper-kind static sites (``site/lib``, ``site/api``, ``site/wiki``).\n\nArgs:\n yml_paths (Iterable[Path]):\n Configuration files to build, in order.\n\nRaises:\n click.ClickException:\n If a configuration file does not exist." }, "serve": { "name": "serve", "kind": "function", "path": "docforge.cli.mkdocs_utils.serve", - "signature": "", + "signature": "", "docstring": "Start an MkDocs development server with live reload.\n\nThe server watches documentation files and automatically reloads\nthe site when changes are detected.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." } } diff --git a/docs/mcp/modules/docforge.cli.mkdocs_utils.json b/docs/mcp/modules/docforge.cli.mkdocs_utils.json index 2ab6539..d655729 100644 --- a/docs/mcp/modules/docforge.cli.mkdocs_utils.json +++ b/docs/mcp/modules/docforge.cli.mkdocs_utils.json @@ -2,15 +2,8 @@ "module": "docforge.cli.mkdocs_utils", "content": { "path": "docforge.cli.mkdocs_utils", - "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A single generated `mkdocs.yml` serves lib, api, and wiki content with\n merged navigation. Wiki navigation, when enabled, precedes every other\n group and its `index.md` becomes the site `Home`.\n\n---", + "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A separate `mkdocs.{kind}.yml` configuration and build is emitted per\n enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and\n written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).\n - Navigation blocks are re-rooted per kind: the wiki navigation drops its\n leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.\n\n---", "objects": { - "os": { - "name": "os", - "kind": "alias", - "path": "docforge.cli.mkdocs_utils.os", - "signature": "", - "docstring": null - }, "Iterable": { "name": "Iterable", "kind": "alias", @@ -32,6 +25,20 @@ "signature": "", "docstring": null }, + "Any": { + "name": "Any", + "kind": "alias", + "path": "docforge.cli.mkdocs_utils.Any", + "signature": "", + "docstring": null + }, + "cast": { + "name": "cast", + "kind": "alias", + "path": "docforge.cli.mkdocs_utils.cast", + "signature": "", + "docstring": null + }, "click": { "name": "click", "kind": "alias", @@ -147,28 +154,49 @@ "name": "generate_sources", "kind": "function", "path": "docforge.cli.mkdocs_utils.generate_sources", - "signature": "", + "signature": "", "docstring": "Generate MkDocs Markdown sources for a Python module.\n\nThis function introspects the specified module, builds the internal\ndocumentation model, and renders Markdown documentation files for\nuse with MkDocs.\n\nArgs:\n module (str):\n Python module import path used as the entry point for\n documentation generation.\n\n docs_dir (Path):\n Directory where the generated Markdown files will be written.\n\n project_name (str | None):\n Optional override for the project name used in documentation metadata.\n\n module_is_source (bool | None):\n If True, treat the specified module directory as the project root\n rather than a nested module.\n\n readme_dir (Path | None):\n Directory where the generated README.md should be written. If not\n provided, defaults to the parent of ``docs_dir``." }, - "generate_config": { - "name": "generate_config", + "build_lib_nav": { + "name": "build_lib_nav", "kind": "function", - "path": "docforge.cli.mkdocs_utils.generate_config", - "signature": "", - "docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found." + "path": "docforge.cli.mkdocs_utils.build_lib_nav", + "signature": "", + "docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found." }, - "build": { - "name": "build", + "build_wiki_nav_block": { + "name": "build_wiki_nav_block", "kind": "function", - "path": "docforge.cli.mkdocs_utils.build", - "signature": "", - "docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." + "path": "docforge.cli.mkdocs_utils.build_wiki_nav_block", + "signature": "", + "docstring": "Build the re-rooted navigation block for a wiki site.\n\nThe wiki navigation derived from the wiki file structure is re-rooted\nrelative to the wiki directory itself by stripping the leading ``wiki/``\nscope component.\n\nArgs:\n wiki_dir (Path):\n Path to the hand-written wiki directory, for example ``docs/wiki``.\n\nReturns:\n list[dict[str, Any]]:\n Navigation entries relative to the wiki directory.\n\nRaises:\n click.FileError:\n If the wiki directory does not exist." + }, + "load_spec_icon": { + "name": "load_spec_icon", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.load_spec_icon", + "signature": "", + "docstring": "Load the theme icon mapping from a navigation specification.\n\nArgs:\n nav_file (Path):\n Path to the navigation specification file.\n\nReturns:\n dict[str, str] | None:\n The icon mapping, or ``None`` when the specification file is\n absent or cannot be parsed." + }, + "generate_site_config": { + "name": "generate_site_config", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.generate_site_config", + "signature": "", + "docstring": "Generate a per-kind `mkdocs.{kind}.yml` configuration file.\n\nThe configuration is created by merging the shared ``mkdocs.common.yml``\ntemplate with the fragment contributed by the kind (``lib``, ``api``, or\n``wiki``). Both ``docs_dir`` and ``site_dir`` are written relative to the\nconfiguration file's directory: the kind's sources when expressed as a\nsibling path (for example ``lib``) and the per-kind site output (for\nexample ``../site/lib``).\n\nArgs:\n kind (str):\n Documentation kind, one of ``lib``, ``api``, or ``wiki``.\n\n kind_root (Path):\n Directory scoped to the kind (for example ``docs/lib``) that\n serves as the MkDocs ``docs_dir``.\n\n nav_block (list[dict[str, Any]]):\n Re-rooted navigation entries for the kind's site.\n\n out (Path):\n Destination path where the generated ``mkdocs.{kind}.yml`` file\n is written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n docs_dir (str):\n MkDocs ``docs_dir`` value, relative to the configuration\n file's directory.\n\n site_dir (str):\n MkDocs ``site_dir`` value, relative to the configuration\n file's directory.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template\n that replaces the built-in templates entirely.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n theme_icon (dict[str, str] | None):\n Optional mapping of theme icon entries injected as\n ``theme.icon``." + }, + "build_configs": { + "name": "build_configs", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.build_configs", + "signature": "", + "docstring": "Build the MkDocs documentation site for every given configuration.\n\nEach configuration file is loaded and built in turn, producing the\nper-kind static sites (``site/lib``, ``site/api``, ``site/wiki``).\n\nArgs:\n yml_paths (Iterable[Path]):\n Configuration files to build, in order.\n\nRaises:\n click.ClickException:\n If a configuration file does not exist." }, "serve": { "name": "serve", "kind": "function", "path": "docforge.cli.mkdocs_utils.serve", - "signature": "", + "signature": "", "docstring": "Start an MkDocs development server with live reload.\n\nThe server watches documentation files and automatically reloads\nthe site when changes are detected.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." } } diff --git a/docs/mcp/modules/docforge.json b/docs/mcp/modules/docforge.json index 6cabd48..17b68ad 100644 --- a/docs/mcp/modules/docforge.json +++ b/docs/mcp/modules/docforge.json @@ -2,7 +2,7 @@ "module": "docforge", "content": { "path": "docforge", - "docstring": "# Summary\n\nRenderer-agnostic Python documentation compiler that converts Python docstrings\ninto structured documentation for both humans (MkDocs) and machines (MCP / AI agents).\n\n`doc-forge` statically analyzes source code, builds a semantic model of modules,\nclasses, functions, and attributes, and renders that model into documentation\noutputs without executing user code.\n\n---\n\n# Installation\n\nInstall using pip:\n\n```bash\npip install doc-forge\n```\n\n---\n\n# CLI usage\n\n## Generate an MkDocs site from a Python package:\n\n```bash\ndoc-forge build --mkdocs --module my_package\n```\n\n## Generate MCP JSON documentation:\n\n```bash\ndoc-forge build --mcp --module my_package\n```\n\n\n## Generate MkDocs site and MCP JSON documentation:\n\n```bash\ndoc-forge build --mcp --mkdocs --module my_package\n```\n\n## Include a hand-written wiki in the MkDocs site:\n\n```bash\ndoc-forge build --wiki --mkdocs --module my_package\n```\n\n## Build wiki pages only (no module required):\n\n```bash\ndoc-forge build --wiki --site-name my_package\n```\n\n## Serve MkDocs locally:\n\n```bash\ndoc-forge serve --mkdocs --module my_package\n```\n\n## Serve MCP locally:\n\n```bash\ndoc-forge serve --mcp --module my_package\n```\n\n---\n\n# Core concepts\n\n## Loader\nExtracts symbols, signatures, and docstrings using static analysis.\n\n## Semantic model\nStructured, renderer-agnostic representation of the API.\n\n## Renderer\nConverts the semantic model into output formats such as MkDocs or MCP JSON.\n\n## Symbol\nAny documentable object\n\n- module\n- class\n- function\n- method\n- property\n- attribute\n\n---\n\n# Architecture\n\n`doc-forge` follows a compiler architecture:\n\n## Front-end:\n\nStatic analysis of modules, classes, functions, type hints, and docstrings.\n\n## Middle-end:\n\nBuilds a semantic model describing symbols and relationships.\n\n## Back-end:\n\nRenders documentation using interchangeable renderers.\n\nThis architecture ensures deterministic documentation generation.\n\n---\n\n# Rendering pipeline\n\nTypical flow:\n\n Python package\n |\n Loader (static analysis)\n |\n Semantic model\n |\n Renderer\n |\n MkDocs site or MCP JSON\n\n---\n\n# Google-Styled Doc-Forge Convention (GSDFC)\n\nGSDFC defines how docstrings must be written so they render correctly in MkDocs and remain machine-parsable by doc-forge and AI tooling.\n\n- Docstrings are the single source of truth.\n- `doc-forge` compiles docstrings but does not generate documentation content.\n- Documentation follows the Python import hierarchy.\n- Every public symbol should have a complete and accurate docstring.\n\n---\n\n## General rules\n\n- Use **Markdown headings** at package and module level.\n- Use **Google-style structured sections** at class, function, and method level.\n- Use type hints in signatures.\n- Use parenthesized types in prose entries (`name (Type):`) that match the\n signature types. This keeps docstrings self-contained and machine-parseable.\n- Write summaries in imperative form.\n- Sections are separated by `---`\n\n---\n\n# Notes subsection grouping\n\nGroup related information using labeled subsections.\n\nExample:\n\n Notes:\n **Guarantees:**\n\n - deterministic behavior\n\n **Lifecycle:**\n\n - created during initialization\n - reused across executions\n\n **Thread safety:**\n\n - safe for concurrent reads\n\n---\n\n# Example formatting\n\n- Use indentation for examples.\n- Indent section contents using four spaces.\n- Use code blocks for example code.\n\nExample:\n Single example:\n\n Example:\n\n ```python\n foo = Foo(\"example\")\n process(foo, multiplier=2)\n ```\n\n Multiple examples:\n\n Example:\n Create foo:\n\n ```python\n foo = Foo(\"example\")\n ```\n\n Run engine:\n\n ```python\n engine = BarEngine([foo])\n engine.run()\n ```\n\nAvoid fenced code blocks inside argument descriptions and other prose lines.\n\nInside `Example:` sections, fenced `python` code blocks are allowed and must be\nindented four spaces, matching the examples below.\n\n---\n\n# Separator rules\n\nUse horizontal separators only at docstring root level to separate sections:\n\n```markdown\n---\n```\n\nAllowed locations:\n\n- package docstrings\n- module docstrings\n- major documentation sections\n\nDo not use separators inside code sections.\n\n---\n\n# Package docstrings\n\nPackage docstrings act as the documentation home page.\n\nRecommended sections:\n\n # Summary\n # Installation\n # Quick start\n # CLI usage\n # Core concepts\n # Architecture\n # Rendering pipeline\n # Examples\n # Notes\n\nExample:\n Package Doc String:\n\n '''\n # Summary\n\n Foo-bar processing framework.\n\n Provides tools for defining Foo objects and executing Bar pipelines.\n\n ---\n\n # Installation\n\n ```bash\n pip install foo-bar\n ```\n\n ---\n\n # Quick start\n\n ```python\n from foobar import Foo, BarEngine\n\n foo = Foo(\"example\")\n engine = BarEngine([foo])\n\n result = engine.run()\n ```\n\n ---\n '''\n\n---\n\n# Module docstrings\n\nModule docstrings describe a subsystem.\n\nRecommended sections:\n\n # Summary\n # Examples\n # Notes\n\nExample:\n Module Doc String:\n\n '''\n # Summary\n\n Foo execution subsystem.\n\n Provides utilities for executing Foo objects through Bar stages.\n\n ---\n\n Example:\n\n ```python\n from foobar.engine import BarEngine\n from foobar.foo import Foo\n\n foo = Foo(\"example\")\n\n engine = BarEngine([foo])\n engine.run()\n ```\n\n ---\n '''\n\n---\n\n# Class docstrings\n\nClass docstrings define object responsibility, lifecycle, and attributes.\n\nRecommended sections:\n\n Attributes:\n Notes:\n Example:\n Raises:\n\nExample:\n Simple Foo:\n\n ```python\n class Foo:\n '''\n Represents a unit of work.\n\n Attributes:\n name (str):\n Identifier of the foo instance.\n\n value (int):\n Numeric value associated with foo.\n\n Notes:\n Guarantees:\n\n - instances are immutable after creation\n\n Lifecycle:\n\n - create instance\n - pass to processing engine\n\n Example:\n Create and inspect a Foo:\n\n ```python\n foo = Foo(\"example\", value=42)\n print(foo.name)\n ```\n '''\n ```\n\n Complex Bar:\n\n ```python\n class BarEngine:\n '''\n Executes Foo objects through Bar stages.\n\n Attributes:\n foos (tuple[Foo, ...]):\n Foo instances managed by the engine.\n\n Notes:\n Guarantees:\n\n - deterministic execution order\n\n Example:\n Run engine:\n\n ```python\n foo1 = Foo(\"a\")\n foo2 = Foo(\"b\")\n\n engine = BarEngine([foo1, foo2])\n engine.run()\n ```\n '''\n ```\n\n---\n\n# Function and method docstrings\n\nFunction docstrings define API contracts.\n\nRecommended sections:\n\n Args:\n Returns:\n Raises:\n Yields:\n Notes:\n Example:\n\nExample:\n Simple process method:\n\n ```python\n def process(foo: Foo, multiplier: int) -> int:\n '''\n Process a Foo instance.\n\n Args:\n foo (Foo):\n Foo instance to process.\n\n multiplier (int):\n Value used to scale foo.\n\n Returns:\n int:\n Processed result.\n\n Raises:\n ValueError:\n If multiplier is negative.\n\n Notes:\n Guarantees:\n\n - foo is not modified\n\n Example:\n Process foo:\n\n ```python\n foo = Foo(\"example\", value=10)\n\n result = process(foo, multiplier=2)\n print(result)\n ```\n '''\n ```\n\n Multiple Examples:\n\n ```python\n def combine(foo_a: Foo, foo_b: Foo) -> Foo:\n '''\n Combine two Foo instances.\n\n Args:\n foo_a (Foo):\n First foo.\n\n foo_b (Foo):\n Second foo.\n\n Returns:\n Foo:\n Combined foo.\n\n Example:\n Basic usage:\n\n ```python\n foo1 = Foo(\"a\")\n foo2 = Foo(\"b\")\n\n combined = combine(foo1, foo2)\n ```\n\n Pipeline usage:\n\n ```python\n engine = BarEngine([foo1, foo2])\n engine.run()\n ```\n '''\n ```\n\n---\n\n# Property docstrings\n\nProperties must document return values.\n\nExample:\n Property Doc String:\n\n ```python\n @property\n def foos(self) -> tuple[Foo, ...]:\n '''\n Return contained Foo instances.\n\n Returns:\n tuple[Foo, ...]:\n Stored foo objects.\n\n Example:\n ```python\n container = FooContainer()\n\n foos = container.foos\n ```\n '''\n ```\n\n---\n\n# Attribute documentation\n\nDocument attributes in class docstrings using `Attributes:`.\n\nExample:\n Attribute Doc String:\n\n ```python\n '''\n Represents a processing stage.\n\n Attributes:\n id (str):\n Unique identifier.\n\n enabled (bool):\n Whether the stage is active.\n '''\n ```\n\n---\n\n# Parsing guarantees\n\nGSDFC ensures doc-forge can deterministically extract:\n\n- symbol kind (module, class, function, property, attribute)\n- symbol name\n- parameters\n- return values\n- attributes\n- examples\n- structured Notes subsections\n\nThis enables:\n\n- reliable MkDocs rendering\n- deterministic MCP export\n- accurate AI semantic interpretation\n\n---\n\nNotes:\n - doc-forge never executes analyzed modules.\n - Documentation is generated entirely through static analysis.", + "docstring": "# Summary\n\nRenderer-agnostic Python documentation compiler that converts Python docstrings\ninto structured documentation for both humans (MkDocs) and machines (MCP / AI agents).\n\n`doc-forge` statically analyzes source code, builds a semantic model of modules,\nclasses, functions, and attributes, and renders that model into documentation\noutputs without executing user code.\n\n---\n\n# Installation\n\nInstall using pip:\n\n```bash\npip install doc-forge\n```\n\n---\n\n# CLI usage\n\nEach site kind (`lib`, `api`, `wiki`) is built independently into `site/{kind}`.\n\n## Build the library reference from a Python package:\n\n```bash\ndoc-forge build --mkdocs --module my_package\n```\n\n## Build the API reference from an OpenAPI spec:\n\n```bash\ndoc-forge build --api --openapi-spec spec.json\n```\n\n## Build the hand-written wiki:\n\n```bash\ndoc-forge build --wiki --site-name my_package\n```\n\n## Generate MCP JSON documentation:\n\n```bash\ndoc-forge build --mcp --module my_package\n```\n\n## Build several kinds in one pass:\n\n```bash\ndoc-forge build --mcp --mkdocs --wiki --module my_package\n```\n\nEach enabled kind gets its own MkDocs config (`docs/mkdocs.{lib,api,wiki}.yml`)\nand its own site under `site/`.\n\n## Serve a site locally:\n\n```bash\ndoc-forge serve --wiki # preview from docs/mkdocs.wiki.yml\ndoc-forge serve --lib\ndoc-forge serve --api\n# or any config directly:\ndoc-forge serve --mkdocs --mkdocs-yml docs/mkdocs.wiki.yml\n```\n\n## Serve MCP locally:\n\n```bash\ndoc-forge serve --mcp --module my_package\n```\n\n---\n\n# Core concepts\n\n## Loader\nExtracts symbols, signatures, and docstrings using static analysis.\n\n## Semantic model\nStructured, renderer-agnostic representation of the API.\n\n## Renderer\nConverts the semantic model into output formats such as MkDocs or MCP JSON.\n\n## Symbol\nAny documentable object\n\n- module\n- class\n- function\n- method\n- property\n- attribute\n\n---\n\n# Architecture\n\n`doc-forge` follows a compiler architecture:\n\n## Front-end:\n\nStatic analysis of modules, classes, functions, type hints, and docstrings.\n\n## Middle-end:\n\nBuilds a semantic model describing symbols and relationships.\n\n## Back-end:\n\nRenders documentation using interchangeable renderers.\n\nThis architecture ensures deterministic documentation generation.\n\n---\n\n# Rendering pipeline\n\nTypical flow:\n\n Python package\n |\n Loader (static analysis)\n |\n Semantic model\n |\n Renderer\n |\n MkDocs site or MCP JSON\n\n---\n\n# Google-Styled Doc-Forge Convention (GSDFC)\n\nGSDFC defines how docstrings must be written so they render correctly in MkDocs and remain machine-parsable by doc-forge and AI tooling.\n\n- Docstrings are the single source of truth.\n- `doc-forge` compiles docstrings but does not generate documentation content.\n- Documentation follows the Python import hierarchy.\n- Every public symbol should have a complete and accurate docstring.\n\n---\n\n## General rules\n\n- Use **Markdown headings** at package and module level.\n- Use **Google-style structured sections** at class, function, and method level.\n- Use type hints in signatures.\n- Use parenthesized types in prose entries (`name (Type):`) that match the\n signature types. This keeps docstrings self-contained and machine-parseable.\n- Write summaries in imperative form.\n- Sections are separated by `---`\n\n---\n\n# Notes subsection grouping\n\nGroup related information using labeled subsections.\n\nExample:\n\n Notes:\n **Guarantees:**\n\n - deterministic behavior\n\n **Lifecycle:**\n\n - created during initialization\n - reused across executions\n\n **Thread safety:**\n\n - safe for concurrent reads\n\n---\n\n# Example formatting\n\n- Use indentation for examples.\n- Indent section contents using four spaces.\n- Use code blocks for example code.\n\nExample:\n Single example:\n\n Example:\n\n ```python\n foo = Foo(\"example\")\n process(foo, multiplier=2)\n ```\n\n Multiple examples:\n\n Example:\n Create foo:\n\n ```python\n foo = Foo(\"example\")\n ```\n\n Run engine:\n\n ```python\n engine = BarEngine([foo])\n engine.run()\n ```\n\nAvoid fenced code blocks inside argument descriptions and other prose lines.\n\nInside `Example:` sections, fenced `python` code blocks are allowed and must be\nindented four spaces, matching the examples below.\n\n---\n\n# Separator rules\n\nUse horizontal separators only at docstring root level to separate sections:\n\n```markdown\n---\n```\n\nAllowed locations:\n\n- package docstrings\n- module docstrings\n- major documentation sections\n\nDo not use separators inside code sections.\n\n---\n\n# Package docstrings\n\nPackage docstrings act as the documentation home page.\n\nRecommended sections:\n\n # Summary\n # Installation\n # Quick start\n # CLI usage\n # Core concepts\n # Architecture\n # Rendering pipeline\n # Examples\n # Notes\n\nExample:\n Package Doc String:\n\n '''\n # Summary\n\n Foo-bar processing framework.\n\n Provides tools for defining Foo objects and executing Bar pipelines.\n\n ---\n\n # Installation\n\n ```bash\n pip install foo-bar\n ```\n\n ---\n\n # Quick start\n\n ```python\n from foobar import Foo, BarEngine\n\n foo = Foo(\"example\")\n engine = BarEngine([foo])\n\n result = engine.run()\n ```\n\n ---\n '''\n\n---\n\n# Module docstrings\n\nModule docstrings describe a subsystem.\n\nRecommended sections:\n\n # Summary\n # Examples\n # Notes\n\nExample:\n Module Doc String:\n\n '''\n # Summary\n\n Foo execution subsystem.\n\n Provides utilities for executing Foo objects through Bar stages.\n\n ---\n\n Example:\n\n ```python\n from foobar.engine import BarEngine\n from foobar.foo import Foo\n\n foo = Foo(\"example\")\n\n engine = BarEngine([foo])\n engine.run()\n ```\n\n ---\n '''\n\n---\n\n# Class docstrings\n\nClass docstrings define object responsibility, lifecycle, and attributes.\n\nRecommended sections:\n\n Attributes:\n Notes:\n Example:\n Raises:\n\nExample:\n Simple Foo:\n\n ```python\n class Foo:\n '''\n Represents a unit of work.\n\n Attributes:\n name (str):\n Identifier of the foo instance.\n\n value (int):\n Numeric value associated with foo.\n\n Notes:\n Guarantees:\n\n - instances are immutable after creation\n\n Lifecycle:\n\n - create instance\n - pass to processing engine\n\n Example:\n Create and inspect a Foo:\n\n ```python\n foo = Foo(\"example\", value=42)\n print(foo.name)\n ```\n '''\n ```\n\n Complex Bar:\n\n ```python\n class BarEngine:\n '''\n Executes Foo objects through Bar stages.\n\n Attributes:\n foos (tuple[Foo, ...]):\n Foo instances managed by the engine.\n\n Notes:\n Guarantees:\n\n - deterministic execution order\n\n Example:\n Run engine:\n\n ```python\n foo1 = Foo(\"a\")\n foo2 = Foo(\"b\")\n\n engine = BarEngine([foo1, foo2])\n engine.run()\n ```\n '''\n ```\n\n---\n\n# Function and method docstrings\n\nFunction docstrings define API contracts.\n\nRecommended sections:\n\n Args:\n Returns:\n Raises:\n Yields:\n Notes:\n Example:\n\nExample:\n Simple process method:\n\n ```python\n def process(foo: Foo, multiplier: int) -> int:\n '''\n Process a Foo instance.\n\n Args:\n foo (Foo):\n Foo instance to process.\n\n multiplier (int):\n Value used to scale foo.\n\n Returns:\n int:\n Processed result.\n\n Raises:\n ValueError:\n If multiplier is negative.\n\n Notes:\n Guarantees:\n\n - foo is not modified\n\n Example:\n Process foo:\n\n ```python\n foo = Foo(\"example\", value=10)\n\n result = process(foo, multiplier=2)\n print(result)\n ```\n '''\n ```\n\n Multiple Examples:\n\n ```python\n def combine(foo_a: Foo, foo_b: Foo) -> Foo:\n '''\n Combine two Foo instances.\n\n Args:\n foo_a (Foo):\n First foo.\n\n foo_b (Foo):\n Second foo.\n\n Returns:\n Foo:\n Combined foo.\n\n Example:\n Basic usage:\n\n ```python\n foo1 = Foo(\"a\")\n foo2 = Foo(\"b\")\n\n combined = combine(foo1, foo2)\n ```\n\n Pipeline usage:\n\n ```python\n engine = BarEngine([foo1, foo2])\n engine.run()\n ```\n '''\n ```\n\n---\n\n# Property docstrings\n\nProperties must document return values.\n\nExample:\n Property Doc String:\n\n ```python\n @property\n def foos(self) -> tuple[Foo, ...]:\n '''\n Return contained Foo instances.\n\n Returns:\n tuple[Foo, ...]:\n Stored foo objects.\n\n Example:\n ```python\n container = FooContainer()\n\n foos = container.foos\n ```\n '''\n ```\n\n---\n\n# Attribute documentation\n\nDocument attributes in class docstrings using `Attributes:`.\n\nExample:\n Attribute Doc String:\n\n ```python\n '''\n Represents a processing stage.\n\n Attributes:\n id (str):\n Unique identifier.\n\n enabled (bool):\n Whether the stage is active.\n '''\n ```\n\n---\n\n# Parsing guarantees\n\nGSDFC ensures doc-forge can deterministically extract:\n\n- symbol kind (module, class, function, property, attribute)\n- symbol name\n- parameters\n- return values\n- attributes\n- examples\n- structured Notes subsections\n\nThis enables:\n\n- reliable MkDocs rendering\n- deterministic MCP export\n- accurate AI semantic interpretation\n\n---\n\nNotes:\n - doc-forge never executes analyzed modules.\n - Documentation is generated entirely through static analysis.", "objects": { "GriffeLoader": { "name": "GriffeLoader", @@ -247,8 +247,15 @@ "kind": "module", "path": "docforge.cli.commands", "signature": null, - "docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` share a single MkDocs build; `--mcp`\n generates a machine-readable bundle independently.\n\n---", + "docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` each emit their own MkDocs config and\n build (`docs/mkdocs.{kind}.yml` into `site/{kind}`); `--mcp` generates a\n machine-readable bundle independently.\n\n---", "members": { + "os": { + "name": "os", + "kind": "alias", + "path": "docforge.cli.commands.os", + "signature": "", + "docstring": null + }, "Path": { "name": "Path", "kind": "alias", @@ -490,15 +497,8 @@ "kind": "module", "path": "docforge.cli.commands.mkdocs_utils", "signature": "", - "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A single generated `mkdocs.yml` serves lib, api, and wiki content with\n merged navigation. Wiki navigation, when enabled, precedes every other\n group and its `index.md` becomes the site `Home`.\n\n---", + "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A separate `mkdocs.{kind}.yml` configuration and build is emitted per\n enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and\n written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).\n - Navigation blocks are re-rooted per kind: the wiki navigation drops its\n leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.\n\n---", "members": { - "os": { - "name": "os", - "kind": "alias", - "path": "docforge.cli.commands.mkdocs_utils.os", - "signature": "", - "docstring": null - }, "Iterable": { "name": "Iterable", "kind": "alias", @@ -520,6 +520,20 @@ "signature": "", "docstring": null }, + "Any": { + "name": "Any", + "kind": "alias", + "path": "docforge.cli.commands.mkdocs_utils.Any", + "signature": "", + "docstring": null + }, + "cast": { + "name": "cast", + "kind": "alias", + "path": "docforge.cli.commands.mkdocs_utils.cast", + "signature": "", + "docstring": null + }, "click": { "name": "click", "kind": "alias", @@ -638,19 +652,40 @@ "signature": "", "docstring": "Generate MkDocs Markdown sources for a Python module.\n\nThis function introspects the specified module, builds the internal\ndocumentation model, and renders Markdown documentation files for\nuse with MkDocs.\n\nArgs:\n module (str):\n Python module import path used as the entry point for\n documentation generation.\n\n docs_dir (Path):\n Directory where the generated Markdown files will be written.\n\n project_name (str | None):\n Optional override for the project name used in documentation metadata.\n\n module_is_source (bool | None):\n If True, treat the specified module directory as the project root\n rather than a nested module.\n\n readme_dir (Path | None):\n Directory where the generated README.md should be written. If not\n provided, defaults to the parent of ``docs_dir``." }, - "generate_config": { - "name": "generate_config", + "build_lib_nav": { + "name": "build_lib_nav", "kind": "function", - "path": "docforge.cli.commands.mkdocs_utils.generate_config", - "signature": "", - "docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found." + "path": "docforge.cli.commands.mkdocs_utils.build_lib_nav", + "signature": "", + "docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found." }, - "build": { - "name": "build", + "build_wiki_nav_block": { + "name": "build_wiki_nav_block", "kind": "function", - "path": "docforge.cli.commands.mkdocs_utils.build", - "signature": "", - "docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." + "path": "docforge.cli.commands.mkdocs_utils.build_wiki_nav_block", + "signature": "", + "docstring": "Build the re-rooted navigation block for a wiki site.\n\nThe wiki navigation derived from the wiki file structure is re-rooted\nrelative to the wiki directory itself by stripping the leading ``wiki/``\nscope component.\n\nArgs:\n wiki_dir (Path):\n Path to the hand-written wiki directory, for example ``docs/wiki``.\n\nReturns:\n list[dict[str, Any]]:\n Navigation entries relative to the wiki directory.\n\nRaises:\n click.FileError:\n If the wiki directory does not exist." + }, + "load_spec_icon": { + "name": "load_spec_icon", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.load_spec_icon", + "signature": "", + "docstring": "Load the theme icon mapping from a navigation specification.\n\nArgs:\n nav_file (Path):\n Path to the navigation specification file.\n\nReturns:\n dict[str, str] | None:\n The icon mapping, or ``None`` when the specification file is\n absent or cannot be parsed." + }, + "generate_site_config": { + "name": "generate_site_config", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.generate_site_config", + "signature": "", + "docstring": "Generate a per-kind `mkdocs.{kind}.yml` configuration file.\n\nThe configuration is created by merging the shared ``mkdocs.common.yml``\ntemplate with the fragment contributed by the kind (``lib``, ``api``, or\n``wiki``). Both ``docs_dir`` and ``site_dir`` are written relative to the\nconfiguration file's directory: the kind's sources when expressed as a\nsibling path (for example ``lib``) and the per-kind site output (for\nexample ``../site/lib``).\n\nArgs:\n kind (str):\n Documentation kind, one of ``lib``, ``api``, or ``wiki``.\n\n kind_root (Path):\n Directory scoped to the kind (for example ``docs/lib``) that\n serves as the MkDocs ``docs_dir``.\n\n nav_block (list[dict[str, Any]]):\n Re-rooted navigation entries for the kind's site.\n\n out (Path):\n Destination path where the generated ``mkdocs.{kind}.yml`` file\n is written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n docs_dir (str):\n MkDocs ``docs_dir`` value, relative to the configuration\n file's directory.\n\n site_dir (str):\n MkDocs ``site_dir`` value, relative to the configuration\n file's directory.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template\n that replaces the built-in templates entirely.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n theme_icon (dict[str, str] | None):\n Optional mapping of theme icon entries injected as\n ``theme.icon``." + }, + "build_configs": { + "name": "build_configs", + "kind": "function", + "path": "docforge.cli.commands.mkdocs_utils.build_configs", + "signature": "", + "docstring": "Build the MkDocs documentation site for every given configuration.\n\nEach configuration file is loaded and built in turn, producing the\nper-kind static sites (``site/lib``, ``site/api``, ``site/wiki``).\n\nArgs:\n yml_paths (Iterable[Path]):\n Configuration files to build, in order.\n\nRaises:\n click.ClickException:\n If a configuration file does not exist." }, "serve": { "name": "serve", @@ -767,21 +802,21 @@ "name": "build", "kind": "function", "path": "docforge.cli.commands.build", - "signature": "", - "docstring": "Build documentation artifacts.\n\nThis command runs the full documentation pipeline: it loads Python\nmodules, generates renderer-specific documentation sources, and\noptionally builds or serves the final output.\n\nDepending on the selected options, the build can target:\n\n- MkDocs static documentation sites for library reference docs\n- Swagger-enabled API docs generated from an OpenAPI spec\n- Hand-written wiki pages included in the MkDocs site\n- MCP structured documentation resources\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` are combined into a single MkDocs\n build, while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable MkDocs library documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Include a hand-written wiki directory in the MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the MkDocs site.\n\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n mkdocs_yml (Path):\n Output path for the generated MkDocs configuration.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting." + "signature": "", + "docstring": "Build documentation artifacts.\n\nThis command runs the full documentation pipeline: it loads Python\nmodules, generates renderer-specific documentation sources, and\noptionally builds the final output.\n\nDepending on the selected options, the build can target:\n\n- A lib MkDocs site (`--mkdocs`) for library reference docs\n- A swagger-enabled API MkDocs site (`--api`) built from an OpenAPI spec\n- A wiki MkDocs site (`--wiki`) built from hand-written markdown\n- MCP structured documentation resources (`--mcp`)\n\nEach enabled site kind produces its own MkDocs configuration\n(`docs/mkdocs.{kind}.yml`) and its own build (`site/{kind}`).\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` emit independent MkDocs builds,\n while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable the lib MkDocs documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Build a hand-written wiki directory as its own MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the lib and wiki MkDocs sites.\n\n docs_dir (Path):\n Shared documentation root used for generated sources.\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting." }, "serve": { "name": "serve", "kind": "function", "path": "docforge.cli.commands.serve", - "signature": "", - "docstring": "Serve generated documentation locally.\n\nDepending on the selected mode, this command starts either:\n\n- A MkDocs development server for browsing documentation\n- An MCP server exposing structured documentation resources\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided." + "signature": "", + "docstring": "Serve generated documentation locally.\n\nDepending on the selected mode, this command starts either:\n\n- A MkDocs development server for browsing a site, or\n- An MCP server exposing structured documentation resources\n\nThe kind flags (`--lib`, `--api`, `--wiki`) select the generated\nper-kind config (`docs/mkdocs.{kind}.yml`); `--mkdocs` serves the config\npassed via `--mkdocs-yml`.\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site from ``--mkdocs-yml``.\n\n lib (bool):\n Serve the lib MkDocs site.\n\n api (bool):\n Serve the API MkDocs site.\n\n wiki (bool):\n Serve the wiki MkDocs site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided." }, "tree": { "name": "tree", "kind": "function", "path": "docforge.cli.commands.tree", - "signature": "", + "signature": "", "docstring": "Display the documentation object tree for a module.\n\nThis command introspects the specified module and prints a\nhierarchical representation of the discovered documentation\nobjects, including modules, classes, functions, and members.\n\nArgs:\n module (str):\n Python module import path to introspect.\n\n project_name (str | None):\n Optional name to display as the project root." }, "Group": { @@ -918,15 +953,8 @@ "kind": "module", "path": "docforge.cli.mkdocs_utils", "signature": null, - "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A single generated `mkdocs.yml` serves lib, api, and wiki content with\n merged navigation. Wiki navigation, when enabled, precedes every other\n group and its `index.md` becomes the site `Home`.\n\n---", + "docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A separate `mkdocs.{kind}.yml` configuration and build is emitted per\n enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and\n written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).\n - Navigation blocks are re-rooted per kind: the wiki navigation drops its\n leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.\n\n---", "members": { - "os": { - "name": "os", - "kind": "alias", - "path": "docforge.cli.mkdocs_utils.os", - "signature": "", - "docstring": null - }, "Iterable": { "name": "Iterable", "kind": "alias", @@ -948,6 +976,20 @@ "signature": "", "docstring": null }, + "Any": { + "name": "Any", + "kind": "alias", + "path": "docforge.cli.mkdocs_utils.Any", + "signature": "", + "docstring": null + }, + "cast": { + "name": "cast", + "kind": "alias", + "path": "docforge.cli.mkdocs_utils.cast", + "signature": "", + "docstring": null + }, "click": { "name": "click", "kind": "alias", @@ -1063,28 +1105,49 @@ "name": "generate_sources", "kind": "function", "path": "docforge.cli.mkdocs_utils.generate_sources", - "signature": "", + "signature": "", "docstring": "Generate MkDocs Markdown sources for a Python module.\n\nThis function introspects the specified module, builds the internal\ndocumentation model, and renders Markdown documentation files for\nuse with MkDocs.\n\nArgs:\n module (str):\n Python module import path used as the entry point for\n documentation generation.\n\n docs_dir (Path):\n Directory where the generated Markdown files will be written.\n\n project_name (str | None):\n Optional override for the project name used in documentation metadata.\n\n module_is_source (bool | None):\n If True, treat the specified module directory as the project root\n rather than a nested module.\n\n readme_dir (Path | None):\n Directory where the generated README.md should be written. If not\n provided, defaults to the parent of ``docs_dir``." }, - "generate_config": { - "name": "generate_config", + "build_lib_nav": { + "name": "build_lib_nav", "kind": "function", - "path": "docforge.cli.mkdocs_utils.generate_config", - "signature": "", - "docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found." + "path": "docforge.cli.mkdocs_utils.build_lib_nav", + "signature": "", + "docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found." }, - "build": { - "name": "build", + "build_wiki_nav_block": { + "name": "build_wiki_nav_block", "kind": "function", - "path": "docforge.cli.mkdocs_utils.build", - "signature": "", - "docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." + "path": "docforge.cli.mkdocs_utils.build_wiki_nav_block", + "signature": "", + "docstring": "Build the re-rooted navigation block for a wiki site.\n\nThe wiki navigation derived from the wiki file structure is re-rooted\nrelative to the wiki directory itself by stripping the leading ``wiki/``\nscope component.\n\nArgs:\n wiki_dir (Path):\n Path to the hand-written wiki directory, for example ``docs/wiki``.\n\nReturns:\n list[dict[str, Any]]:\n Navigation entries relative to the wiki directory.\n\nRaises:\n click.FileError:\n If the wiki directory does not exist." + }, + "load_spec_icon": { + "name": "load_spec_icon", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.load_spec_icon", + "signature": "", + "docstring": "Load the theme icon mapping from a navigation specification.\n\nArgs:\n nav_file (Path):\n Path to the navigation specification file.\n\nReturns:\n dict[str, str] | None:\n The icon mapping, or ``None`` when the specification file is\n absent or cannot be parsed." + }, + "generate_site_config": { + "name": "generate_site_config", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.generate_site_config", + "signature": "", + "docstring": "Generate a per-kind `mkdocs.{kind}.yml` configuration file.\n\nThe configuration is created by merging the shared ``mkdocs.common.yml``\ntemplate with the fragment contributed by the kind (``lib``, ``api``, or\n``wiki``). Both ``docs_dir`` and ``site_dir`` are written relative to the\nconfiguration file's directory: the kind's sources when expressed as a\nsibling path (for example ``lib``) and the per-kind site output (for\nexample ``../site/lib``).\n\nArgs:\n kind (str):\n Documentation kind, one of ``lib``, ``api``, or ``wiki``.\n\n kind_root (Path):\n Directory scoped to the kind (for example ``docs/lib``) that\n serves as the MkDocs ``docs_dir``.\n\n nav_block (list[dict[str, Any]]):\n Re-rooted navigation entries for the kind's site.\n\n out (Path):\n Destination path where the generated ``mkdocs.{kind}.yml`` file\n is written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n docs_dir (str):\n MkDocs ``docs_dir`` value, relative to the configuration\n file's directory.\n\n site_dir (str):\n MkDocs ``site_dir`` value, relative to the configuration\n file's directory.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template\n that replaces the built-in templates entirely.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n theme_icon (dict[str, str] | None):\n Optional mapping of theme icon entries injected as\n ``theme.icon``." + }, + "build_configs": { + "name": "build_configs", + "kind": "function", + "path": "docforge.cli.mkdocs_utils.build_configs", + "signature": "", + "docstring": "Build the MkDocs documentation site for every given configuration.\n\nEach configuration file is loaded and built in turn, producing the\nper-kind static sites (``site/lib``, ``site/api``, ``site/wiki``).\n\nArgs:\n yml_paths (Iterable[Path]):\n Configuration files to build, in order.\n\nRaises:\n click.ClickException:\n If a configuration file does not exist." }, "serve": { "name": "serve", "kind": "function", "path": "docforge.cli.mkdocs_utils.serve", - "signature": "", + "signature": "", "docstring": "Start an MkDocs development server with live reload.\n\nThe server watches documentation files and automatically reloads\nthe site when changes are detected.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist." } } diff --git a/mkdocs.yml b/docs/mkdocs.lib.yml similarity index 63% rename from mkdocs.yml rename to docs/mkdocs.lib.yml index bf187fa..c19dbe6 100644 --- a/mkdocs.yml +++ b/docs/mkdocs.lib.yml @@ -67,40 +67,32 @@ plugins: show_object_full_path: false show_symbol_type_heading: true site_name: docforge -docs_dir: docs +docs_dir: lib +site_dir: ../site/lib nav: -- Home: wiki/index.md -- Overview: wiki/01_overview.md -- Architecture: wiki/02_architecture.md -- Conventions: wiki/03_conventions.md -- Iterative Workflow: wiki/04_iterative_workflow.md -- Development: - - Environment: wiki/05_development/01_environment.md - - Quality Gates: wiki/05_development/02_quality_gates.md - - Gsdfc Guide: wiki/05_development/03_gsdfc_guide.md - - Mcp Guide: wiki/05_development/04_mcp_guide.md +- Home: index.md - Loaders: - - lib/loaders/index.md - - lib/loaders/griffe_loader.md + - loaders/index.md + - loaders/griffe_loader.md - Models: - - lib/models/index.md - - lib/models/module.md - - lib/models/object.md - - lib/models/project.md + - models/index.md + - models/module.md + - models/object.md + - models/project.md - Navigation: - - lib/nav/index.md - - lib/nav/spec.md - - lib/nav/resolver.md - - lib/nav/mkdocs.md + - nav/index.md + - nav/spec.md + - nav/resolver.md + - nav/mkdocs.md - Renderers: - - lib/renderers/index.md - - lib/renderers/base.md - - lib/renderers/mkdocs_renderer.md - - lib/renderers/mcp_renderer.md + - renderers/index.md + - renderers/base.md + - renderers/mkdocs_renderer.md + - renderers/mcp_renderer.md - CLI: - - lib/cli/index.md - - lib/cli/main.md - - lib/cli/commands.md - - lib/cli/api_utils.md - - lib/cli/mcp_utils.md - - lib/cli/mkdocs_utils.md + - cli/index.md + - cli/main.md + - cli/commands.md + - cli/api_utils.md + - cli/mcp_utils.md + - cli/mkdocs_utils.md diff --git a/docs/mkdocs.wiki.yml b/docs/mkdocs.wiki.yml new file mode 100644 index 0000000..f490763 --- /dev/null +++ b/docs/mkdocs.wiki.yml @@ -0,0 +1,64 @@ +theme: + name: material + palette: + - scheme: slate + primary: deep purple + accent: cyan + font: + text: Inter + code: JetBrains Mono + features: + - navigation.sections + - navigation.expand + - navigation.top + - navigation.instant + - navigation.tracking + - navigation.indexes + - content.code.copy + - content.code.annotate + - content.tabs.link + - content.action.edit + - search.highlight + - search.share + - search.suggest + icon: + logo: material/file-document-multiple + repo: fontawesome/brands/github +markdown_extensions: +- pymdownx.superfences +- pymdownx.inlinehilite +- pymdownx.snippets +- admonition +- pymdownx.details +- pymdownx.highlight: + linenums: true + anchor_linenums: true + line_spans: __span + pygments_lang_class: true +- pymdownx.tabbed: + alternate_style: true +- pymdownx.tasklist: + custom_checkbox: true +- tables +- footnotes +- pymdownx.caret +- pymdownx.tilde +- pymdownx.mark +extra_css: +- https://unpkg.com/dracula-prism/dist/css/dracula-prism.css +plugins: +- search +site_name: docforge +docs_dir: wiki +site_dir: ../site/wiki +nav: +- Home: index.md +- Overview: 01_overview.md +- Architecture: 02_architecture.md +- Conventions: 03_conventions.md +- Iterative Workflow: 04_iterative_workflow.md +- Development: + - Environment: 05_development/01_environment.md + - Quality Gates: 05_development/02_quality_gates.md + - Gsdfc Guide: 05_development/03_gsdfc_guide.md + - Mcp Guide: 05_development/04_mcp_guide.md diff --git a/docs/wiki/01_overview.md b/docs/wiki/01_overview.md index d011ed9..25797be 100644 --- a/docs/wiki/01_overview.md +++ b/docs/wiki/01_overview.md @@ -2,7 +2,7 @@ `docforge` turns GSDFC-compliant Python docstrings into maintainable reference documentation. It never edits source docstrings; it reads them, renders them, -and assembles a single MkDocs site from all available material. +and assembles documentation sites from all available material. ## What it generates @@ -13,16 +13,17 @@ and assembles a single MkDocs site from all available material. | `wiki` | Hand-written markdown | `docs/wiki/**` (unchanged) | | `mcp` | GSDFC docstrings + renderers | `docs/mcp/**` structured JSON | -## Combined build +## Per-kind MkDocs builds -One `mkdocs.yml` and one MkDocs build serve all kinds: +Each enabled site kind produces its own MkDocs configuration and build: -1. Wiki navigation is derived from the `docs/wiki/` file structure. -2. Generated library/API navigation is appended after it. -3. The wiki `index.md` becomes the site `Home`. +1. `--mkdocs` (lib) writes `docs/mkdocs.lib.yml` and builds `site/lib/`. +2. `--api` writes `docs/mkdocs.api.yml` and builds `site/api/`. +3. `--wiki` writes `docs/mkdocs.wiki.yml` and builds `site/wiki/`. -Hand-written wiki content is never overwritten or regenerated — only its -navigation is derived automatically. +Every site is self-contained (own theme assets and search index) with +navigation derived or scoped to that kind. Hand-written wiki content is never +overwritten or regenerated — only its navigation is derived automatically. ## MCP bundle diff --git a/docs/wiki/02_architecture.md b/docs/wiki/02_architecture.md index 36dd93f..0a72c9e 100644 --- a/docs/wiki/02_architecture.md +++ b/docs/wiki/02_architecture.md @@ -35,6 +35,6 @@ file structure via `build_wiki_nav`. - `commands.py` — the `build` command and its `--mkdocs` / `--api` / `--wiki` / `--mcp` modes -- `mkdocs_utils.py` — config generation (`mkdocs.yml`) including merged - wiki + lib + api navigation +- `mkdocs_utils.py` — per-kind config generation (`docs/mkdocs.{lib,api,wiki}.yml`) + with navigation re-rooted to each kind's `docs_dir` - `api_utils.py` — OpenAPI loading and API docs generation \ No newline at end of file diff --git a/docs/wiki/04_iterative_workflow.md b/docs/wiki/04_iterative_workflow.md index aec8108..94a355e 100644 --- a/docs/wiki/04_iterative_workflow.md +++ b/docs/wiki/04_iterative_workflow.md @@ -8,7 +8,7 @@ A docs build runs through the CLI in a single pass. # Library reference only doc-forge build --mkdocs --module docforge -# Wiki + library combined (single MkDocs build) +# Wiki + library (each an independent MkDocs build) doc-forge build --wiki --mkdocs --module docforge # Wiki only — no module required @@ -18,21 +18,26 @@ doc-forge build --wiki --site-name docforge doc-forge build --mcp --module docforge ``` -## What a combined build does +## What a build does 1. Validates the requested modes (`--mkdocs`, `--api`, `--wiki`, `--mcp`). 2. Generates library sources under `docs/lib/**` with `MkDocsRenderer`. 3. Generates API sources under `docs/api/**` when `--api` is given. -4. Derives wiki navigation from `docs/wiki/**`. -5. Writes `mkdocs.yml` with merged navigation — wiki first, generated groups - appended, and the wiki `Home` replacing any spec `Home` entry. -6. Runs `mkdocs build` once and emits the site. +4. Derives the wiki navigation from `docs/wiki/**`. +5. Writes one MkDocs config per site kind (`docs/mkdocs.{lib,api,wiki}.yml`), + re-rooting navigation paths to each kind's `docs_dir`. +6. Runs `mkdocs build` once per config, emitting self-contained sites + `site/lib/`, `site/api/`, and `site/wiki/`. -## Explore the site +## Explore the sites ```bash doc-forge build --wiki --mkdocs --module docforge -doc-forge serve --mkdocs --mkdocs-yml mkdocs.yml +doc-forge serve --wiki # serves site preview from docs/mkdocs.wiki.yml +doc-forge serve --lib +doc-forge serve --api +# or target any config directly: +doc-forge serve --mkdocs --mkdocs-yml docs/mkdocs.wiki.yml ``` ## Serve the MCP bundle diff --git a/docs/wiki/index.md b/docs/wiki/index.md index dcae17a..d66e3eb 100644 --- a/docs/wiki/index.md +++ b/docs/wiki/index.md @@ -2,7 +2,7 @@ `docforge` is an internal documentation tool that generates reference documentation from Google-Styled Doc-Forge Convention (GSDFC) docstrings and -assembles it into a single MkDocs site, alongside hand-written wiki pages and +assembles it into per-kind MkDocs sites, alongside hand-written wiki pages and OpenAPI-based API docs. --- @@ -11,7 +11,7 @@ OpenAPI-based API docs. - 🧪 GSDFC docstring extraction via Griffe - 🧭 Auto-derived navigation for hand-written wiki pages -- 🗂 Combined MkDocs build: wiki first, then generated library/API reference +- 🗂 Independent MkDocs builds per kind (lib, api, wiki) into `site/{kind}` - 🔌 Material MkDocs theming out of the box - 📦 MCP structured documentation resources diff --git a/tests/cli/test_build_api.py b/tests/cli/test_build_api.py index 3489e31..83c7446 100644 --- a/tests/cli/test_build_api.py +++ b/tests/cli/test_build_api.py @@ -1,4 +1,4 @@ -import json +import json from pathlib import Path from docforge.cli.main import cli @@ -55,8 +55,10 @@ def test_api_build_full_flow( index = (api_dir / "index.md").read_text(encoding="utf-8") assert '' in index - config = (cwd / "mkdocs.yml").read_text(encoding="utf-8") - assert "docs_dir: docs" in config + config = (cwd / "docs" / "mkdocs.api.yml").read_text(encoding="utf-8") + assert "docs_dir: api" in config + assert "site_dir: ../site/api" in config + assert "- API Reference: index.md" in config assert "site_name: Aetoskia Auth Server" in config assert "site_description: Auth docs" in config assert "site_author: Aetoskia Dev Team" in config @@ -90,10 +92,9 @@ def test_api_build_rejects_site_name_override(cli_runner): assert "cannot be overridden" in result.output -def test_api_build_combined_with_mkdocs_allows_site_name_for_lib( +def test_api_build_combined_with_mkdocs_emits_separate_configs( cli_runner, mock_mkdocs_build, mock_mkdocs_load_config ): - # site_name is accepted when --mkdocs is also present (lib mode owns it) with cli_runner.isolated_filesystem(): cwd = Path.cwd() @@ -105,13 +106,7 @@ def test_api_build_combined_with_mkdocs_allows_site_name_for_lib( spec_path = _write_spec(cwd) nav_file = cwd / "docforge.nav.yml" - nav_file.write_text( - "home: lib/testpkg/index.md\n" - "groups:\n" - " API:\n" - " - api/index.md\n", - encoding="utf-8", - ) + nav_file.write_text("home: lib/testpkg/index.md\ngroups: {}\n") result = cli_runner.invoke( cli, @@ -128,7 +123,20 @@ def test_api_build_combined_with_mkdocs_allows_site_name_for_lib( assert result.exit_code == 0, result.output - config = (cwd / "mkdocs.yml").read_text(encoding="utf-8") - assert "docs_dir: docs" in config - assert "swagger-ui-tag" in config - assert "mkdocstrings" in config + lib_config = cwd / "docs" / "mkdocs.lib.yml" + api_config = cwd / "docs" / "mkdocs.api.yml" + assert lib_config.exists() + assert api_config.exists() + + lib_text = lib_config.read_text(encoding="utf-8") + assert "docs_dir: lib" in lib_text + assert "site_dir: ../site/lib" in lib_text + assert "Home: testpkg/index.md" in lib_text + + api_text = api_config.read_text(encoding="utf-8") + assert "docs_dir: api" in api_text + assert "site_dir: ../site/api" in api_text + assert "- API Reference: index.md" in api_text + assert "site_name: Aetoskia Auth Server" in api_text + assert "swagger-ui-tag" in api_text + assert "mkdocstrings" in lib_text diff --git a/tests/cli/test_build_mkdocs.py b/tests/cli/test_build_mkdocs.py index 501011b..368110d 100644 --- a/tests/cli/test_build_mkdocs.py +++ b/tests/cli/test_build_mkdocs.py @@ -20,10 +20,6 @@ def test_mkdocs_build_full_flow( nav_file = cwd / "docforge.nav.yml" nav_file.write_text("home: lib/testpkg/index.md\ngroups: {}\n") - # We need to create a dummy testpkg/index.md for nav resolution if it's there - # But generate_sources will create it. - # Wait, the current logic runs generate_sources first, THEN generate_config. - result = cli_runner.invoke( cli, [ @@ -33,16 +29,19 @@ def test_mkdocs_build_full_flow( "testpkg", "--site-name", "Test Site", - "--mkdocs-yml", - "mkdocs.yml", ], ) assert result.exit_code == 0 assert mock_mkdocs_build() is True - assert (cwd / "mkdocs.yml").exists() + + config = cwd / "docs" / "mkdocs.lib.yml" + assert config.exists() assert (cwd / "docs" / "lib" / "testpkg" / "mod.md").exists() - assert "docs_dir: docs" in (cwd / "mkdocs.yml").read_text() + content = config.read_text(encoding="utf-8") + assert "docs_dir: lib" in content + assert "site_dir: ../site/lib" in content + assert "Home: testpkg/index.md" in content def test_mkdocs_build_missing_module_fails(cli_runner): @@ -66,7 +65,7 @@ def test_mkdocs_build_without_site_name_uses_module_as_default_full_flow( (pkg / "__init__.py").write_text("") (pkg / "mod.py").write_text("def f(): ...\n") - # Create nav spec expected by generate_config + # Create nav spec expected by build_lib_nav nav_file = cwd / "docforge.nav.yml" nav_file.write_text( "home: lib/testpkg/index.md\ngroups: {}\n", @@ -75,25 +74,18 @@ def test_mkdocs_build_without_site_name_uses_module_as_default_full_flow( result = cli_runner.invoke( cli, - [ - "build", - "--mkdocs", - "--module", - "testpkg", - "--mkdocs-yml", - "mkdocs.yml", - ], + ["build", "--mkdocs", "--module", "testpkg"], ) assert result.exit_code == 0 assert mock_mkdocs_build() is True - # MkDocs config must exist - mkdocs_yml = cwd / "mkdocs.yml" - assert mkdocs_yml.exists() + # The per-kind MkDocs config must exist + config = cwd / "docs" / "mkdocs.lib.yml" + assert config.exists() # Site name must default to module name - content = mkdocs_yml.read_text() + content = config.read_text(encoding="utf-8") assert "site_name: testpkg" in content # Docs must be generated under the nested docs/lib dir diff --git a/tests/cli/test_build_wiki.py b/tests/cli/test_build_wiki.py index 5b0827a..60a8f04 100644 --- a/tests/cli/test_build_wiki.py +++ b/tests/cli/test_build_wiki.py @@ -23,22 +23,22 @@ def test_wiki_only_build_requires_no_module( "--wiki", "--site-name", "Wiki Site", - "--mkdocs-yml", - "mkdocs.yml", ], ) assert result.exit_code == 0, result.output assert mock_mkdocs_build() is True - assert (cwd / "mkdocs.yml").exists() + config = cwd / "docs" / "mkdocs.wiki.yml" + assert config.exists() - content = (cwd / "mkdocs.yml").read_text(encoding="utf-8") - assert "docs_dir: docs" in content - assert "Home: wiki/index.md" in content - assert "Overview: wiki/01_overview.md" in content + content = config.read_text(encoding="utf-8") + assert "docs_dir: wiki" in content + assert "site_dir: ../site/wiki" in content + assert "Home: index.md" in content + assert "Overview: 01_overview.md" in content -def test_wiki_combined_with_mkdocs_keeps_lib_nav( +def test_wiki_and_mkdocs_emit_separate_configs( cli_runner, mock_mkdocs_build, mock_mkdocs_load_config, @@ -70,9 +70,7 @@ def test_wiki_combined_with_mkdocs_keeps_lib_nav( "--module", "testpkg", "--site-name", - "Combined Site", - "--mkdocs-yml", - "mkdocs.yml", + "Split Site", ], ) @@ -80,11 +78,16 @@ def test_wiki_combined_with_mkdocs_keeps_lib_nav( assert mock_mkdocs_build() is True assert (cwd / "docs" / "lib" / "testpkg" / "mod.md").exists() - content = (cwd / "mkdocs.yml").read_text(encoding="utf-8") - assert "docs_dir: docs" in content - assert "Home: wiki/index.md" in content - # Wiki home replaces the lib home entry - assert "Home: lib/testpkg/index.md" not in content + lib_config = (cwd / "docs" / "mkdocs.lib.yml").read_text(encoding="utf-8") + wiki_config = (cwd / "docs" / "mkdocs.wiki.yml").read_text(encoding="utf-8") + + assert "docs_dir: lib" in lib_config + assert "site_dir: ../site/lib" in lib_config + assert "Home: testpkg/index.md" in lib_config + + assert "docs_dir: wiki" in wiki_config + assert "site_dir: ../site/wiki" in wiki_config + assert "Home: index.md" in wiki_config def test_wiki_without_module_flag_ok( @@ -122,8 +125,6 @@ def test_missing_wiki_dir_errors(cli_runner, mock_mkdocs_build): "S", "--wiki-dir", "docs/wiki", - "--mkdocs-yml", - "mkdocs.yml", ], ) diff --git a/tests/cli/test_mkdocs_utils.py b/tests/cli/test_mkdocs_utils.py new file mode 100644 index 0000000..6e1ccea --- /dev/null +++ b/tests/cli/test_mkdocs_utils.py @@ -0,0 +1,79 @@ +import pytest +from click.exceptions import FileError + +from docforge.cli.mkdocs_utils import ( + _strip_scope, + build_lib_nav, + build_wiki_nav_block, + load_spec_icon, +) + + +def test_strip_scope_nested_entries(): + block = [ + {"Home": "wiki/index.md"}, + { + "Development": [ + {"Environment": "wiki/05_development/01_environment.md"}, + "wiki/05_development/02_quality_gates.md", + ] + }, + ] + stripped = _strip_scope(block, "wiki") + assert stripped == [ + {"Home": "index.md"}, + { + "Development": [ + {"Environment": "05_development/01_environment.md"}, + "05_development/02_quality_gates.md", + ] + }, + ] + + +def test_strip_scope_leaves_unscoped_paths(): + block = [{"Other": "static/pages.md"}] + assert _strip_scope(block, "lib") == [{"Other": "static/pages.md"}] + + +def test_build_lib_nav_reroots_group_paths(tmp_path): + docs = tmp_path / "docs" + lib = docs / "lib" / "testpkg" + lib.mkdir(parents=True) + (lib / "index.md").write_text("# Pkg", encoding="utf-8") + (lib / "mod.md").write_text("# Mod", encoding="utf-8") + + nav_file = tmp_path / "docforge.nav.yml" + nav_file.write_text( + "home: lib/testpkg/index.md\n" + "groups:\n" + " Reference:\n" + " - lib/testpkg/mod.md\n", + encoding="utf-8", + ) + + block, icon = build_lib_nav(nav_file, docs) + assert block == [{"Home": "testpkg/index.md"}, {"Reference": ["testpkg/mod.md"]}] + assert icon is None + + +def test_build_lib_nav_missing_spec_raises(tmp_path): + with pytest.raises(FileError): + build_lib_nav(tmp_path / "missing.yml", tmp_path) + + +def test_build_wiki_nav_block_reroots_entries(tmp_path): + wiki = tmp_path / "docs" / "wiki" + wiki.mkdir(parents=True) + (wiki / "index.md").write_text("# Home", encoding="utf-8") + (wiki / "01_overview.md").write_text("# Overview", encoding="utf-8") + + block = build_wiki_nav_block(wiki) + assert block == [{"Home": "index.md"}, {"Overview": "01_overview.md"}] + + +def test_load_spec_icon(tmp_path): + nav_file = tmp_path / "docforge.nav.yml" + nav_file.write_text("home: index.md\nicon:\n logo: material/code-tags\n") + assert load_spec_icon(nav_file) == {"logo": "material/code-tags"} + assert load_spec_icon(tmp_path / "missing.yml") is None