feat: build each doc kind with its own MkDocs config and site

This commit is contained in:
2026-09-12 14:23:22 +05:30
parent 582b6809a0
commit 2ae96f58de
21 changed files with 942 additions and 443 deletions

1
.gitignore vendored
View File

@@ -53,6 +53,7 @@ test-results/
site/ site/
docs/_site/ docs/_site/
docs/build/ docs/build/
mkdocs.yml
# ========================= # =========================
# MkDocs / Sphinx output # MkDocs / Sphinx output

View File

@@ -23,41 +23,49 @@ pip install doc-forge
# CLI usage # 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 ```bash
doc-forge build --mkdocs --module my_package 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: ## Generate MCP JSON documentation:
```bash ```bash
doc-forge build --mcp --module my_package doc-forge build --mcp --module my_package
``` ```
## Build several kinds in one pass:
## Generate MkDocs site and MCP JSON documentation:
```bash ```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 ```bash
doc-forge build --wiki --mkdocs --module my_package doc-forge serve --wiki # preview from docs/mkdocs.wiki.yml
``` doc-forge serve --lib
doc-forge serve --api
## Build wiki pages only (no module required): # or any config directly:
doc-forge serve --mkdocs --mkdocs-yml docs/mkdocs.wiki.yml
```bash
doc-forge build --wiki --site-name my_package
```
## Serve MkDocs locally:
```bash
doc-forge serve --mkdocs --module my_package
``` ```
## Serve MCP locally: ## Serve MCP locally:

View File

@@ -22,41 +22,49 @@ pip install doc-forge
# CLI usage # 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 ```bash
doc-forge build --mkdocs --module my_package 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: ## Generate MCP JSON documentation:
```bash ```bash
doc-forge build --mcp --module my_package doc-forge build --mcp --module my_package
``` ```
## Build several kinds in one pass:
## Generate MkDocs site and MCP JSON documentation:
```bash ```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 ```bash
doc-forge build --wiki --mkdocs --module my_package doc-forge serve --wiki # preview from docs/mkdocs.wiki.yml
``` doc-forge serve --lib
doc-forge serve --api
## Build wiki pages only (no module required): # or any config directly:
doc-forge serve --mkdocs --mkdocs-yml docs/mkdocs.wiki.yml
```bash
doc-forge build --wiki --site-name my_package
```
## Serve MkDocs locally:
```bash
doc-forge serve --mkdocs --module my_package
``` ```
## Serve MCP locally: ## Serve MCP locally:

View File

@@ -9,12 +9,14 @@ Provides the CLI structure using Click, including build, serve, and tree command
Notes: Notes:
- The `build` command validates requested modes before generating anything. - The `build` command validates requested modes before generating anything.
- `--mkdocs`, `--api`, and `--wiki` share a single MkDocs build; `--mcp` - `--mkdocs`, `--api`, and `--wiki` each emit their own MkDocs config and
generates a machine-readable bundle independently. build (`docs/mkdocs.{kind}.yml` into `site/{kind}`); `--mcp` generates a
machine-readable bundle independently.
--- ---
""" """
import os
from pathlib import Path from pathlib import Path
import click import click
@@ -37,10 +39,10 @@ def cli() -> None:
@cli.command() @cli.command()
@click.option("--mcp", is_flag=True, help="Build MCP resources") @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("--api", is_flag=True, help="Build API docs from an OpenAPI spec")
@click.option( @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( @click.option(
"--module-is-source", "--module-is-source",
@@ -54,7 +56,7 @@ def cli() -> None:
help="Path to the OpenAPI JSON specification", help="Path to the OpenAPI JSON specification",
) )
@click.option("--project-name", help="Project name override") @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( @click.option(
"--docs-dir", "--docs-dir",
type=click.Path(path_type=Path), type=click.Path(path_type=Path),
@@ -65,7 +67,7 @@ def cli() -> None:
"--wiki-dir", "--wiki-dir",
type=click.Path(path_type=Path), type=click.Path(path_type=Path),
default=Path("docs/wiki"), 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( @click.option(
"--nav", "--nav",
@@ -77,12 +79,6 @@ def cli() -> None:
@click.option( @click.option(
"--template", type=click.Path(path_type=Path), help="MkDocs template path" "--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( @click.option(
"--out-dir", "--out-dir",
type=click.Path(path_type=Path), type=click.Path(path_type=Path),
@@ -103,7 +99,6 @@ def build(
wiki_dir: Path, wiki_dir: Path,
nav_file: Path, nav_file: Path,
template: Path | None, template: Path | None,
mkdocs_yml: Path,
out_dir: Path, out_dir: Path,
) -> None: ) -> None:
""" """
@@ -111,33 +106,36 @@ def build(
This command runs the full documentation pipeline: it loads Python This command runs the full documentation pipeline: it loads Python
modules, generates renderer-specific documentation sources, and 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: Depending on the selected options, the build can target:
- MkDocs static documentation sites for library reference docs - A lib MkDocs site (`--mkdocs`) for library reference docs
- Swagger-enabled API docs generated from an OpenAPI spec - A swagger-enabled API MkDocs site (`--api`) built from an OpenAPI spec
- Hand-written wiki pages included in the MkDocs site - A wiki MkDocs site (`--wiki`) built from hand-written markdown
- MCP structured documentation resources - 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: Notes:
- At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be
provided. provided.
- `--mkdocs`, `--api`, and `--wiki` are combined into a single MkDocs - `--mkdocs`, `--api`, and `--wiki` emit independent MkDocs builds,
build, while `--mcp` emits a machine-readable bundle. while `--mcp` emits a machine-readable bundle.
Args: Args:
mcp (bool): mcp (bool):
Enable MCP documentation generation. Enable MCP documentation generation.
mkdocs (bool): mkdocs (bool):
Enable MkDocs library documentation generation. Enable the lib MkDocs documentation generation.
api (bool): api (bool):
Enable API documentation generation from an OpenAPI spec. Enable API documentation generation from an OpenAPI spec.
wiki (bool): 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): module_is_source (bool):
Treat the specified module directory as the project root. Treat the specified module directory as the project root.
@@ -152,11 +150,10 @@ def build(
Optional override for the project name. Optional override for the project name.
site_name (str | None): site_name (str | None):
Display name for the MkDocs site. Display name for the lib and wiki MkDocs sites.
docs_dir (Path): docs_dir (Path):
Shared documentation root used as the MkDocs ``docs_dir``. Shared documentation root used for generated sources.
wiki_dir (Path): wiki_dir (Path):
Directory containing hand-written wiki markdown files. Directory containing hand-written wiki markdown files.
@@ -166,9 +163,6 @@ def build(
template (Path | None): template (Path | None):
Optional custom MkDocs configuration template. Optional custom MkDocs configuration template.
mkdocs_yml (Path):
Output path for the generated MkDocs configuration.
out_dir (Path): out_dir (Path):
Output directory for generated MCP resources. Output directory for generated MCP resources.
@@ -188,18 +182,18 @@ def build(
"the OpenAPI spec provides the site name" "the OpenAPI spec provides the site name"
) )
if (mkdocs or mcp) and not module: if mkdocs and not module:
raise click.UsageError( raise click.UsageError("--module is required for MkDocs build")
"--module is required for MkDocs build" if mcp and not module:
if mkdocs raise click.UsageError("--module is required for MCP build")
else "--module is required for MCP build"
)
spec: dict | None = None spec: dict | None = None
if api: if api:
spec = api_utils.load_openapi_spec(openapi_spec) spec = api_utils.load_openapi_spec(openapi_spec)
kinds: list[str] = []
if mkdocs: if mkdocs:
kinds.append("lib")
lib_dir = docs_dir / "lib" lib_dir = docs_dir / "lib"
click.echo(f"Generating MkDocs sources in {lib_dir}...") click.echo(f"Generating MkDocs sources in {lib_dir}...")
mkdocs_utils.generate_sources( mkdocs_utils.generate_sources(
@@ -207,54 +201,64 @@ def build(
lib_dir, lib_dir,
project_name, project_name,
module_is_source, module_is_source,
readme_dir=mkdocs_yml.parent, readme_dir=Path(".").resolve(),
) )
if api: if api:
kinds.append("api")
api_dir = docs_dir / "api" api_dir = docs_dir / "api"
click.echo(f"Generating API sources in {api_dir}...") click.echo(f"Generating API sources in {api_dir}...")
api_utils.generate_api_sources(spec, api_dir) api_utils.generate_api_sources(spec, api_dir)
if mkdocs or api or wiki: if wiki:
modes: list[str] = [] kinds.append("wiki")
if mkdocs:
modes.append("lib") if kinds:
api_metadata: api_utils.OpenAPIMetadata | None = None
if api: if api:
modes.append("api") api_metadata = api_utils.derive_metadata(spec)
if wiki: theme_icon = mkdocs_utils.load_spec_icon(nav_file)
modes.append("wiki")
site_description: str | None = None config_paths: list[Path] = []
site_author: str | None = None for kind in kinds:
effective_site_name = site_name or module or Path.cwd().name kind_root = wiki_dir if kind == "wiki" else docs_dir / kind
if api: site_name_kind = site_name or module or Path.cwd().name
metadata = api_utils.derive_metadata(spec) site_description: str | None = None
effective_site_name = metadata.site_name site_author: str | None = None
site_description = metadata.site_description if kind == "api" and api_metadata:
site_author = metadata.site_author 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}...") if kind == "lib":
mkdocs_utils.generate_config( nav_block, _kind_icon = mkdocs_utils.build_lib_nav(nav_file, docs_dir)
docs_dir, elif kind == "wiki":
nav_file, nav_block = mkdocs_utils.build_wiki_nav_block(wiki_dir)
template, else:
mkdocs_yml, nav_block = [{"API Reference": "index.md"}]
effective_site_name,
modes=modes,
site_description=site_description,
site_author=site_author,
wiki_dir=wiki_dir if wiki else None,
)
click.echo("Running MkDocs build...") out = docs_dir / f"mkdocs.{kind}.yml"
mkdocs_utils.build(mkdocs_yml) click.echo(f"Generating MkDocs config {out}...")
click.echo("MkDocs build completed.") 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 mcp:
if not module:
raise click.UsageError("--module is required for MCP build")
click.echo(f"Generating MCP resources in {out_dir}...") click.echo(f"Generating MCP resources in {out_dir}...")
mcp_utils.generate_resources(module, project_name, out_dir) mcp_utils.generate_resources(module, project_name, out_dir)
click.echo("MCP build completed.") click.echo("MCP build completed.")
@@ -262,12 +266,15 @@ def build(
@cli.command() @cli.command()
@click.option("--mcp", is_flag=True, help="Serve MCP documentation") @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("--module", help="Python module to serve")
@click.option( @click.option(
"--mkdocs-yml", "--mkdocs-yml",
type=click.Path(path_type=Path), type=click.Path(path_type=Path),
default=Path("mkdocs.yml"), default=Path("docs/mkdocs.wiki.yml"),
help="MkDocs config path", help="MkDocs config path",
) )
@click.option( @click.option(
@@ -279,6 +286,9 @@ def build(
def serve( def serve(
mcp: bool, mcp: bool,
mkdocs: bool, mkdocs: bool,
lib: bool,
api: bool,
wiki: bool,
module: str | None, module: str | None,
mkdocs_yml: Path, mkdocs_yml: Path,
out_dir: Path, out_dir: Path,
@@ -288,15 +298,28 @@ def serve(
Depending on the selected mode, this command starts either: 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 - 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: Args:
mcp (bool): mcp (bool):
Serve documentation using the MCP server. Serve documentation using the MCP server.
mkdocs (bool): 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): module (str | None):
Python module import path to serve via MCP. Python module import path to serve via MCP.
@@ -311,17 +334,37 @@ def serve(
click.UsageError: click.UsageError:
If invalid or conflicting options are provided. If invalid or conflicting options are provided.
""" """
if mcp and mkdocs: selected = [
raise click.UsageError("Cannot specify both --mcp and --mkdocs") name
if not mcp and not mkdocs: for name, enabled in (
raise click.UsageError("Must specify either --mcp or --mkdocs") ("mcp", mcp),
if mcp and not module: ("mkdocs", mkdocs),
raise click.UsageError("--module is required for MCP serve") ("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: if mcp:
mkdocs_utils.serve(mkdocs_yml) if not module:
elif mcp: raise click.UsageError("--module is required for MCP serve")
mcp_utils.serve(module, out_dir) 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() @cli.command()

View File

@@ -20,12 +20,14 @@ def build(
wiki_dir: Path, wiki_dir: Path,
nav_file: Path, nav_file: Path,
template: Path | None, template: Path | None,
mkdocs_yml: Path,
out_dir: Path, out_dir: Path,
) -> None: ... ) -> None: ...
def serve( def serve(
mcp: bool, mcp: bool,
mkdocs: bool, mkdocs: bool,
lib: bool,
api: bool,
wiki: bool,
module: str | None, module: str | None,
mkdocs_yml: Path, mkdocs_yml: Path,
out_dir: Path, out_dir: Path,

View File

@@ -6,17 +6,19 @@ Utilities for working with MkDocs in the doc-forge CLI.
--- ---
Notes: Notes:
- A single generated `mkdocs.yml` serves lib, api, and wiki content with - A separate `mkdocs.{kind}.yml` configuration and build is emitted per
merged navigation. Wiki navigation, when enabled, precedes every other enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and
group and its `index.md` becomes the site `Home`. 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 collections.abc import Iterable
from importlib import resources from importlib import resources
from pathlib import Path from pathlib import Path
from typing import Any, cast
import click import click
import yaml import yaml
@@ -83,53 +85,171 @@ def generate_sources(
) )
def generate_config( def build_lib_nav(
docs_dir: Path,
nav_file: Path, nav_file: Path,
template: Path | None, docs_root: Path,
out: Path, ) -> tuple[list[dict[str, Any]], dict[str, str] | None]:
site_name: str,
modes: Iterable[str] | None = None,
site_description: str | None = None,
site_author: str | None = None,
wiki_dir: Path | None = None,
) -> 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 The navigation specification is resolved against the shared documentation
with a navigation structure derived from the docforge navigation root and every resulting path is re-rooted relative to the ``lib``
specification (and, when a wiki directory is provided, from the wiki subdirectory by stripping its leading ``lib/`` scope component.
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.
Args: Args:
docs_dir (Path):
Shared documentation root used as the MkDocs ``docs_dir``.
nav_file (Path): nav_file (Path):
Path to the `docforge.nav.yml` navigation specification. Path to the `docforge.nav.yml` navigation specification.
template (Path | None): docs_root (Path):
Optional path to a fully custom MkDocs configuration template. Shared documentation root containing the ``lib`` sources.
If not provided, built-in templates are merged; the provided
template replaces the built-in templates entirely. 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): 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): site_name (str):
Display name for the generated documentation site. Display name for the generated documentation site.
modes (Iterable[str] | None): docs_dir (str):
Documentation modes to enable. Each mode contributes its own MkDocs ``docs_dir`` value, relative to the configuration
built-in template fragment (for example ``lib``, ``api``, or file's directory.
``wiki``), merged on top of the shared ``mkdocs.common.yml``
template. 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): site_description (str | None):
Optional site description written into the configuration. Optional site description written into the configuration.
@@ -137,50 +257,26 @@ def generate_config(
site_author (str | None): site_author (str | None):
Optional site author written into the configuration. Optional site author written into the configuration.
wiki_dir (Path | None): theme_icon (dict[str, str] | None):
Optional path to a hand-written wiki directory (for example Optional mapping of theme icon entries injected as
``docs/wiki``). When provided, the site navigation is derived ``theme.icon``.
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.
""" """
if not nav_file.exists() and wiki_dir is None: data = _load_template(template, [kind])
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["site_name"] = site_name data["site_name"] = site_name
if site_description: if site_description:
data["site_description"] = site_description data["site_description"] = site_description
if site_author: if site_author:
data["site_author"] = 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 data["nav"] = nav_block
if spec is not None and spec.icon: if theme_icon:
theme = data.setdefault("theme", {}) theme = data.setdefault("theme", {})
if not isinstance(theme, dict): if not isinstance(theme, dict):
theme = {} theme = {}
theme["icon"] = spec.icon theme["icon"] = theme_icon
data["theme"] = theme data["theme"] = theme
out.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8") 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 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 Each configuration file is loaded and built in turn, producing the
build command to generate the final static documentation site. per-kind static sites (``site/lib``, ``site/api``, ``site/wiki``).
Args: Args:
mkdocs_yml (Path): yml_paths (Iterable[Path]):
Path to the `mkdocs.yml` configuration file. Configuration files to build, in order.
Raises: Raises:
click.ClickException: 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.commands.build import build as mkdocs_build
from mkdocs.config import load_config 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: def serve(mkdocs_yml: Path) -> None:

View File

@@ -1,5 +1,6 @@
from collections.abc import Iterable from collections.abc import Iterable
from pathlib import Path from pathlib import Path
from typing import Any
def generate_sources( def generate_sources(
module: str, module: str,
@@ -8,16 +9,25 @@ def generate_sources(
module_is_source: bool | None = None, module_is_source: bool | None = None,
readme_dir: Path | None = None, readme_dir: Path | None = None,
) -> None: ... ) -> None: ...
def generate_config( def build_lib_nav(
docs_dir: Path,
nav_file: Path, 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, out: Path,
site_name: str, site_name: str,
modes: Iterable[str] | None = None, docs_dir: str,
site_dir: str,
template: Path | None = None,
site_description: str | None = None, site_description: str | None = None,
site_author: str | None = None, site_author: str | None = None,
wiki_dir: Path | None = None, theme_icon: dict[str, str] | None = None,
) -> None: ... ) -> None: ...
def build(mkdocs_yml: Path) -> None: ... def build_configs(yml_paths: Iterable[Path]) -> None: ...
def serve(mkdocs_yml: Path) -> None: ... def serve(mkdocs_yml: Path) -> None: ...

View File

@@ -2,8 +2,15 @@
"module": "docforge.cli.commands", "module": "docforge.cli.commands",
"content": { "content": {
"path": "docforge.cli.commands", "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": { "objects": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.commands.os",
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
"docstring": null
},
"Path": { "Path": {
"name": "Path", "name": "Path",
"kind": "alias", "kind": "alias",
@@ -245,15 +252,8 @@
"kind": "module", "kind": "module",
"path": "docforge.cli.commands.mkdocs_utils", "path": "docforge.cli.commands.mkdocs_utils",
"signature": "<bound method Alias.signature of Alias('mkdocs_utils', 'docforge.cli.mkdocs_utils')>", "signature": "<bound method Alias.signature of Alias('mkdocs_utils', '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---",
"members": { "members": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.commands.mkdocs_utils.os",
"signature": "<bound method Alias.signature of Alias('os', 'docforge.cli.mkdocs_utils.os')>",
"docstring": null
},
"Iterable": { "Iterable": {
"name": "Iterable", "name": "Iterable",
"kind": "alias", "kind": "alias",
@@ -275,6 +275,20 @@
"signature": "<bound method Alias.signature of Alias('Path', 'docforge.cli.mkdocs_utils.Path')>", "signature": "<bound method Alias.signature of Alias('Path', 'docforge.cli.mkdocs_utils.Path')>",
"docstring": null "docstring": null
}, },
"Any": {
"name": "Any",
"kind": "alias",
"path": "docforge.cli.commands.mkdocs_utils.Any",
"signature": "<bound method Alias.signature of Alias('Any', 'docforge.cli.mkdocs_utils.Any')>",
"docstring": null
},
"cast": {
"name": "cast",
"kind": "alias",
"path": "docforge.cli.commands.mkdocs_utils.cast",
"signature": "<bound method Alias.signature of Alias('cast', 'docforge.cli.mkdocs_utils.cast')>",
"docstring": null
},
"click": { "click": {
"name": "click", "name": "click",
"kind": "alias", "kind": "alias",
@@ -393,19 +407,40 @@
"signature": "<bound method Alias.signature of Alias('generate_sources', 'docforge.cli.mkdocs_utils.generate_sources')>", "signature": "<bound method Alias.signature of Alias('generate_sources', 'docforge.cli.mkdocs_utils.generate_sources')>",
"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``." "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": { "build_lib_nav": {
"name": "generate_config", "name": "build_lib_nav",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.mkdocs_utils.generate_config", "path": "docforge.cli.commands.mkdocs_utils.build_lib_nav",
"signature": "<bound method Alias.signature of Alias('generate_config', 'docforge.cli.mkdocs_utils.generate_config')>", "signature": "<bound method Alias.signature of Alias('build_lib_nav', 'docforge.cli.mkdocs_utils.build_lib_nav')>",
"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." "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": { "build_wiki_nav_block": {
"name": "build", "name": "build_wiki_nav_block",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.mkdocs_utils.build", "path": "docforge.cli.commands.mkdocs_utils.build_wiki_nav_block",
"signature": "<bound method Alias.signature of Alias('build', 'docforge.cli.mkdocs_utils.build')>", "signature": "<bound method Alias.signature of Alias('build_wiki_nav_block', 'docforge.cli.mkdocs_utils.build_wiki_nav_block')>",
"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." "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": "<bound method Alias.signature of Alias('load_spec_icon', 'docforge.cli.mkdocs_utils.load_spec_icon')>",
"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": "<bound method Alias.signature of Alias('generate_site_config', 'docforge.cli.mkdocs_utils.generate_site_config')>",
"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": "<bound method Alias.signature of Alias('build_configs', 'docforge.cli.mkdocs_utils.build_configs')>",
"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": { "serve": {
"name": "serve", "name": "serve",
@@ -522,21 +557,21 @@
"name": "build", "name": "build",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.build", "path": "docforge.cli.commands.build",
"signature": "<bound method Function.signature of Function('build', 38, 260)>", "signature": "<bound method Function.signature of Function('build', 40, 264)>",
"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." "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": { "serve": {
"name": "serve", "name": "serve",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.serve", "path": "docforge.cli.commands.serve",
"signature": "<bound method Function.signature of Function('serve', 263, 324)>", "signature": "<bound method Function.signature of Function('serve', 267, 367)>",
"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." "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": { "tree": {
"name": "tree", "name": "tree",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.tree", "path": "docforge.cli.commands.tree",
"signature": "<bound method Function.signature of Function('tree', 327, 363)>", "signature": "<bound method Function.signature of Function('tree', 370, 406)>",
"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." "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": { "Group": {

View File

@@ -134,8 +134,15 @@
"kind": "module", "kind": "module",
"path": "docforge.cli.commands", "path": "docforge.cli.commands",
"signature": null, "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": { "members": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.commands.os",
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
"docstring": null
},
"Path": { "Path": {
"name": "Path", "name": "Path",
"kind": "alias", "kind": "alias",
@@ -377,15 +384,8 @@
"kind": "module", "kind": "module",
"path": "docforge.cli.commands.mkdocs_utils", "path": "docforge.cli.commands.mkdocs_utils",
"signature": "<bound method Alias.signature of Alias('mkdocs_utils', 'docforge.cli.mkdocs_utils')>", "signature": "<bound method Alias.signature of Alias('mkdocs_utils', '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---",
"members": { "members": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.commands.mkdocs_utils.os",
"signature": "<bound method Alias.signature of Alias('os', 'docforge.cli.mkdocs_utils.os')>",
"docstring": null
},
"Iterable": { "Iterable": {
"name": "Iterable", "name": "Iterable",
"kind": "alias", "kind": "alias",
@@ -407,6 +407,20 @@
"signature": "<bound method Alias.signature of Alias('Path', 'docforge.cli.mkdocs_utils.Path')>", "signature": "<bound method Alias.signature of Alias('Path', 'docforge.cli.mkdocs_utils.Path')>",
"docstring": null "docstring": null
}, },
"Any": {
"name": "Any",
"kind": "alias",
"path": "docforge.cli.commands.mkdocs_utils.Any",
"signature": "<bound method Alias.signature of Alias('Any', 'docforge.cli.mkdocs_utils.Any')>",
"docstring": null
},
"cast": {
"name": "cast",
"kind": "alias",
"path": "docforge.cli.commands.mkdocs_utils.cast",
"signature": "<bound method Alias.signature of Alias('cast', 'docforge.cli.mkdocs_utils.cast')>",
"docstring": null
},
"click": { "click": {
"name": "click", "name": "click",
"kind": "alias", "kind": "alias",
@@ -525,19 +539,40 @@
"signature": "<bound method Alias.signature of Alias('generate_sources', 'docforge.cli.mkdocs_utils.generate_sources')>", "signature": "<bound method Alias.signature of Alias('generate_sources', 'docforge.cli.mkdocs_utils.generate_sources')>",
"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``." "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": { "build_lib_nav": {
"name": "generate_config", "name": "build_lib_nav",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.mkdocs_utils.generate_config", "path": "docforge.cli.commands.mkdocs_utils.build_lib_nav",
"signature": "<bound method Alias.signature of Alias('generate_config', 'docforge.cli.mkdocs_utils.generate_config')>", "signature": "<bound method Alias.signature of Alias('build_lib_nav', 'docforge.cli.mkdocs_utils.build_lib_nav')>",
"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." "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": { "build_wiki_nav_block": {
"name": "build", "name": "build_wiki_nav_block",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.mkdocs_utils.build", "path": "docforge.cli.commands.mkdocs_utils.build_wiki_nav_block",
"signature": "<bound method Alias.signature of Alias('build', 'docforge.cli.mkdocs_utils.build')>", "signature": "<bound method Alias.signature of Alias('build_wiki_nav_block', 'docforge.cli.mkdocs_utils.build_wiki_nav_block')>",
"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." "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": "<bound method Alias.signature of Alias('load_spec_icon', 'docforge.cli.mkdocs_utils.load_spec_icon')>",
"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": "<bound method Alias.signature of Alias('generate_site_config', 'docforge.cli.mkdocs_utils.generate_site_config')>",
"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": "<bound method Alias.signature of Alias('build_configs', 'docforge.cli.mkdocs_utils.build_configs')>",
"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": { "serve": {
"name": "serve", "name": "serve",
@@ -654,21 +689,21 @@
"name": "build", "name": "build",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.build", "path": "docforge.cli.commands.build",
"signature": "<bound method Function.signature of Function('build', 38, 260)>", "signature": "<bound method Function.signature of Function('build', 40, 264)>",
"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." "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": { "serve": {
"name": "serve", "name": "serve",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.serve", "path": "docforge.cli.commands.serve",
"signature": "<bound method Function.signature of Function('serve', 263, 324)>", "signature": "<bound method Function.signature of Function('serve', 267, 367)>",
"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." "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": { "tree": {
"name": "tree", "name": "tree",
"kind": "function", "kind": "function",
"path": "docforge.cli.commands.tree", "path": "docforge.cli.commands.tree",
"signature": "<bound method Function.signature of Function('tree', 327, 363)>", "signature": "<bound method Function.signature of Function('tree', 370, 406)>",
"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." "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": { "Group": {
@@ -805,15 +840,8 @@
"kind": "module", "kind": "module",
"path": "docforge.cli.mkdocs_utils", "path": "docforge.cli.mkdocs_utils",
"signature": null, "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": { "members": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.mkdocs_utils.os",
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
"docstring": null
},
"Iterable": { "Iterable": {
"name": "Iterable", "name": "Iterable",
"kind": "alias", "kind": "alias",
@@ -835,6 +863,20 @@
"signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>", "signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>",
"docstring": null "docstring": null
}, },
"Any": {
"name": "Any",
"kind": "alias",
"path": "docforge.cli.mkdocs_utils.Any",
"signature": "<bound method Alias.signature of Alias('Any', 'typing.Any')>",
"docstring": null
},
"cast": {
"name": "cast",
"kind": "alias",
"path": "docforge.cli.mkdocs_utils.cast",
"signature": "<bound method Alias.signature of Alias('cast', 'typing.cast')>",
"docstring": null
},
"click": { "click": {
"name": "click", "name": "click",
"kind": "alias", "kind": "alias",
@@ -950,28 +992,49 @@
"name": "generate_sources", "name": "generate_sources",
"kind": "function", "kind": "function",
"path": "docforge.cli.mkdocs_utils.generate_sources", "path": "docforge.cli.mkdocs_utils.generate_sources",
"signature": "<bound method Function.signature of Function('generate_sources', 34, 83)>", "signature": "<bound method Function.signature of Function('generate_sources', 36, 85)>",
"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``." "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": { "build_lib_nav": {
"name": "generate_config", "name": "build_lib_nav",
"kind": "function", "kind": "function",
"path": "docforge.cli.mkdocs_utils.generate_config", "path": "docforge.cli.mkdocs_utils.build_lib_nav",
"signature": "<bound method Function.signature of Function('generate_config', 86, 186)>", "signature": "<bound method Function.signature of Function('build_lib_nav', 88, 121)>",
"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." "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": { "build_wiki_nav_block": {
"name": "build", "name": "build_wiki_nav_block",
"kind": "function", "kind": "function",
"path": "docforge.cli.mkdocs_utils.build", "path": "docforge.cli.mkdocs_utils.build_wiki_nav_block",
"signature": "<bound method Function.signature of Function('build', 313, 334)>", "signature": "<bound method Function.signature of Function('build_wiki_nav_block', 124, 147)>",
"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." "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": "<bound method Function.signature of Function('load_spec_icon', 150, 169)>",
"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": "<bound method Function.signature of Function('generate_site_config', 201, 282)>",
"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": "<bound method Function.signature of Function('build_configs', 409, 430)>",
"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": { "serve": {
"name": "serve", "name": "serve",
"kind": "function", "kind": "function",
"path": "docforge.cli.mkdocs_utils.serve", "path": "docforge.cli.mkdocs_utils.serve",
"signature": "<bound method Function.signature of Function('serve', 337, 357)>", "signature": "<bound method Function.signature of Function('serve', 433, 453)>",
"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." "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."
} }
} }

View File

@@ -2,15 +2,8 @@
"module": "docforge.cli.mkdocs_utils", "module": "docforge.cli.mkdocs_utils",
"content": { "content": {
"path": "docforge.cli.mkdocs_utils", "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": { "objects": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.mkdocs_utils.os",
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
"docstring": null
},
"Iterable": { "Iterable": {
"name": "Iterable", "name": "Iterable",
"kind": "alias", "kind": "alias",
@@ -32,6 +25,20 @@
"signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>", "signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>",
"docstring": null "docstring": null
}, },
"Any": {
"name": "Any",
"kind": "alias",
"path": "docforge.cli.mkdocs_utils.Any",
"signature": "<bound method Alias.signature of Alias('Any', 'typing.Any')>",
"docstring": null
},
"cast": {
"name": "cast",
"kind": "alias",
"path": "docforge.cli.mkdocs_utils.cast",
"signature": "<bound method Alias.signature of Alias('cast', 'typing.cast')>",
"docstring": null
},
"click": { "click": {
"name": "click", "name": "click",
"kind": "alias", "kind": "alias",
@@ -147,28 +154,49 @@
"name": "generate_sources", "name": "generate_sources",
"kind": "function", "kind": "function",
"path": "docforge.cli.mkdocs_utils.generate_sources", "path": "docforge.cli.mkdocs_utils.generate_sources",
"signature": "<bound method Function.signature of Function('generate_sources', 34, 83)>", "signature": "<bound method Function.signature of Function('generate_sources', 36, 85)>",
"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``." "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": { "build_lib_nav": {
"name": "generate_config", "name": "build_lib_nav",
"kind": "function", "kind": "function",
"path": "docforge.cli.mkdocs_utils.generate_config", "path": "docforge.cli.mkdocs_utils.build_lib_nav",
"signature": "<bound method Function.signature of Function('generate_config', 86, 186)>", "signature": "<bound method Function.signature of Function('build_lib_nav', 88, 121)>",
"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." "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": { "build_wiki_nav_block": {
"name": "build", "name": "build_wiki_nav_block",
"kind": "function", "kind": "function",
"path": "docforge.cli.mkdocs_utils.build", "path": "docforge.cli.mkdocs_utils.build_wiki_nav_block",
"signature": "<bound method Function.signature of Function('build', 313, 334)>", "signature": "<bound method Function.signature of Function('build_wiki_nav_block', 124, 147)>",
"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." "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": "<bound method Function.signature of Function('load_spec_icon', 150, 169)>",
"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": "<bound method Function.signature of Function('generate_site_config', 201, 282)>",
"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": "<bound method Function.signature of Function('build_configs', 409, 430)>",
"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": { "serve": {
"name": "serve", "name": "serve",
"kind": "function", "kind": "function",
"path": "docforge.cli.mkdocs_utils.serve", "path": "docforge.cli.mkdocs_utils.serve",
"signature": "<bound method Function.signature of Function('serve', 337, 357)>", "signature": "<bound method Function.signature of Function('serve', 433, 453)>",
"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." "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."
} }
} }

File diff suppressed because one or more lines are too long

View File

@@ -67,40 +67,32 @@ plugins:
show_object_full_path: false show_object_full_path: false
show_symbol_type_heading: true show_symbol_type_heading: true
site_name: docforge site_name: docforge
docs_dir: docs docs_dir: lib
site_dir: ../site/lib
nav: nav:
- Home: wiki/index.md - Home: 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
- Loaders: - Loaders:
- lib/loaders/index.md - loaders/index.md
- lib/loaders/griffe_loader.md - loaders/griffe_loader.md
- Models: - Models:
- lib/models/index.md - models/index.md
- lib/models/module.md - models/module.md
- lib/models/object.md - models/object.md
- lib/models/project.md - models/project.md
- Navigation: - Navigation:
- lib/nav/index.md - nav/index.md
- lib/nav/spec.md - nav/spec.md
- lib/nav/resolver.md - nav/resolver.md
- lib/nav/mkdocs.md - nav/mkdocs.md
- Renderers: - Renderers:
- lib/renderers/index.md - renderers/index.md
- lib/renderers/base.md - renderers/base.md
- lib/renderers/mkdocs_renderer.md - renderers/mkdocs_renderer.md
- lib/renderers/mcp_renderer.md - renderers/mcp_renderer.md
- CLI: - CLI:
- lib/cli/index.md - cli/index.md
- lib/cli/main.md - cli/main.md
- lib/cli/commands.md - cli/commands.md
- lib/cli/api_utils.md - cli/api_utils.md
- lib/cli/mcp_utils.md - cli/mcp_utils.md
- lib/cli/mkdocs_utils.md - cli/mkdocs_utils.md

64
docs/mkdocs.wiki.yml Normal file
View File

@@ -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

View File

@@ -2,7 +2,7 @@
`docforge` turns GSDFC-compliant Python docstrings into maintainable reference `docforge` turns GSDFC-compliant Python docstrings into maintainable reference
documentation. It never edits source docstrings; it reads them, renders them, 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 ## What it generates
@@ -13,16 +13,17 @@ and assembles a single MkDocs site from all available material.
| `wiki` | Hand-written markdown | `docs/wiki/**` (unchanged) | | `wiki` | Hand-written markdown | `docs/wiki/**` (unchanged) |
| `mcp` | GSDFC docstrings + renderers | `docs/mcp/**` structured JSON | | `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. 1. `--mkdocs` (lib) writes `docs/mkdocs.lib.yml` and builds `site/lib/`.
2. Generated library/API navigation is appended after it. 2. `--api` writes `docs/mkdocs.api.yml` and builds `site/api/`.
3. The wiki `index.md` becomes the site `Home`. 3. `--wiki` writes `docs/mkdocs.wiki.yml` and builds `site/wiki/`.
Hand-written wiki content is never overwritten or regenerated — only its Every site is self-contained (own theme assets and search index) with
navigation is derived automatically. navigation derived or scoped to that kind. Hand-written wiki content is never
overwritten or regenerated — only its navigation is derived automatically.
## MCP bundle ## MCP bundle

View File

@@ -35,6 +35,6 @@ file structure via `build_wiki_nav`.
- `commands.py` — the `build` command and its `--mkdocs` / `--api` / `--wiki` - `commands.py` — the `build` command and its `--mkdocs` / `--api` / `--wiki`
/ `--mcp` modes / `--mcp` modes
- `mkdocs_utils.py` — config generation (`mkdocs.yml`) including merged - `mkdocs_utils.py` — per-kind config generation (`docs/mkdocs.{lib,api,wiki}.yml`)
wiki + lib + api navigation with navigation re-rooted to each kind's `docs_dir`
- `api_utils.py` — OpenAPI loading and API docs generation - `api_utils.py` — OpenAPI loading and API docs generation

View File

@@ -8,7 +8,7 @@ A docs build runs through the CLI in a single pass.
# Library reference only # Library reference only
doc-forge build --mkdocs --module docforge 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 doc-forge build --wiki --mkdocs --module docforge
# Wiki only — no module required # Wiki only — no module required
@@ -18,21 +18,26 @@ doc-forge build --wiki --site-name docforge
doc-forge build --mcp --module 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`). 1. Validates the requested modes (`--mkdocs`, `--api`, `--wiki`, `--mcp`).
2. Generates library sources under `docs/lib/**` with `MkDocsRenderer`. 2. Generates library sources under `docs/lib/**` with `MkDocsRenderer`.
3. Generates API sources under `docs/api/**` when `--api` is given. 3. Generates API sources under `docs/api/**` when `--api` is given.
4. Derives wiki navigation from `docs/wiki/**`. 4. Derives the wiki navigation from `docs/wiki/**`.
5. Writes `mkdocs.yml` with merged navigation — wiki first, generated groups 5. Writes one MkDocs config per site kind (`docs/mkdocs.{lib,api,wiki}.yml`),
appended, and the wiki `Home` replacing any spec `Home` entry. re-rooting navigation paths to each kind's `docs_dir`.
6. Runs `mkdocs build` once and emits the site. 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 ```bash
doc-forge build --wiki --mkdocs --module docforge 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 ## Serve the MCP bundle

View File

@@ -2,7 +2,7 @@
`docforge` is an internal documentation tool that generates reference `docforge` is an internal documentation tool that generates reference
documentation from Google-Styled Doc-Forge Convention (GSDFC) docstrings and 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. OpenAPI-based API docs.
--- ---
@@ -11,7 +11,7 @@ OpenAPI-based API docs.
- 🧪 GSDFC docstring extraction via Griffe - 🧪 GSDFC docstring extraction via Griffe
- 🧭 Auto-derived navigation for hand-written wiki pages - 🧭 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 - 🔌 Material MkDocs theming out of the box
- 📦 MCP structured documentation resources - 📦 MCP structured documentation resources

View File

@@ -1,4 +1,4 @@
import json import json
from pathlib import Path from pathlib import Path
from docforge.cli.main import cli 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") index = (api_dir / "index.md").read_text(encoding="utf-8")
assert '<swagger-ui src="openapi.json"/>' in index assert '<swagger-ui src="openapi.json"/>' in index
config = (cwd / "mkdocs.yml").read_text(encoding="utf-8") config = (cwd / "docs" / "mkdocs.api.yml").read_text(encoding="utf-8")
assert "docs_dir: docs" in config 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_name: Aetoskia Auth Server" in config
assert "site_description: Auth docs" in config assert "site_description: Auth docs" in config
assert "site_author: Aetoskia Dev Team" 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 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 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(): with cli_runner.isolated_filesystem():
cwd = Path.cwd() cwd = Path.cwd()
@@ -105,13 +106,7 @@ def test_api_build_combined_with_mkdocs_allows_site_name_for_lib(
spec_path = _write_spec(cwd) spec_path = _write_spec(cwd)
nav_file = cwd / "docforge.nav.yml" nav_file = cwd / "docforge.nav.yml"
nav_file.write_text( nav_file.write_text("home: lib/testpkg/index.md\ngroups: {}\n")
"home: lib/testpkg/index.md\n"
"groups:\n"
" API:\n"
" - api/index.md\n",
encoding="utf-8",
)
result = cli_runner.invoke( result = cli_runner.invoke(
cli, cli,
@@ -128,7 +123,20 @@ def test_api_build_combined_with_mkdocs_allows_site_name_for_lib(
assert result.exit_code == 0, result.output assert result.exit_code == 0, result.output
config = (cwd / "mkdocs.yml").read_text(encoding="utf-8") lib_config = cwd / "docs" / "mkdocs.lib.yml"
assert "docs_dir: docs" in config api_config = cwd / "docs" / "mkdocs.api.yml"
assert "swagger-ui-tag" in config assert lib_config.exists()
assert "mkdocstrings" in config 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

View File

@@ -20,10 +20,6 @@ def test_mkdocs_build_full_flow(
nav_file = cwd / "docforge.nav.yml" nav_file = cwd / "docforge.nav.yml"
nav_file.write_text("home: lib/testpkg/index.md\ngroups: {}\n") 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( result = cli_runner.invoke(
cli, cli,
[ [
@@ -33,16 +29,19 @@ def test_mkdocs_build_full_flow(
"testpkg", "testpkg",
"--site-name", "--site-name",
"Test Site", "Test Site",
"--mkdocs-yml",
"mkdocs.yml",
], ],
) )
assert result.exit_code == 0 assert result.exit_code == 0
assert mock_mkdocs_build() is True 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 (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): 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 / "__init__.py").write_text("")
(pkg / "mod.py").write_text("def f(): ...\n") (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 = cwd / "docforge.nav.yml"
nav_file.write_text( nav_file.write_text(
"home: lib/testpkg/index.md\ngroups: {}\n", "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( result = cli_runner.invoke(
cli, cli,
[ ["build", "--mkdocs", "--module", "testpkg"],
"build",
"--mkdocs",
"--module",
"testpkg",
"--mkdocs-yml",
"mkdocs.yml",
],
) )
assert result.exit_code == 0 assert result.exit_code == 0
assert mock_mkdocs_build() is True assert mock_mkdocs_build() is True
# MkDocs config must exist # The per-kind MkDocs config must exist
mkdocs_yml = cwd / "mkdocs.yml" config = cwd / "docs" / "mkdocs.lib.yml"
assert mkdocs_yml.exists() assert config.exists()
# Site name must default to module name # 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 assert "site_name: testpkg" in content
# Docs must be generated under the nested docs/lib dir # Docs must be generated under the nested docs/lib dir

View File

@@ -23,22 +23,22 @@ def test_wiki_only_build_requires_no_module(
"--wiki", "--wiki",
"--site-name", "--site-name",
"Wiki Site", "Wiki Site",
"--mkdocs-yml",
"mkdocs.yml",
], ],
) )
assert result.exit_code == 0, result.output assert result.exit_code == 0, result.output
assert mock_mkdocs_build() is True 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") content = config.read_text(encoding="utf-8")
assert "docs_dir: docs" in content assert "docs_dir: wiki" in content
assert "Home: wiki/index.md" in content assert "site_dir: ../site/wiki" in content
assert "Overview: wiki/01_overview.md" 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, cli_runner,
mock_mkdocs_build, mock_mkdocs_build,
mock_mkdocs_load_config, mock_mkdocs_load_config,
@@ -70,9 +70,7 @@ def test_wiki_combined_with_mkdocs_keeps_lib_nav(
"--module", "--module",
"testpkg", "testpkg",
"--site-name", "--site-name",
"Combined Site", "Split Site",
"--mkdocs-yml",
"mkdocs.yml",
], ],
) )
@@ -80,11 +78,16 @@ def test_wiki_combined_with_mkdocs_keeps_lib_nav(
assert mock_mkdocs_build() is True assert mock_mkdocs_build() is True
assert (cwd / "docs" / "lib" / "testpkg" / "mod.md").exists() assert (cwd / "docs" / "lib" / "testpkg" / "mod.md").exists()
content = (cwd / "mkdocs.yml").read_text(encoding="utf-8") lib_config = (cwd / "docs" / "mkdocs.lib.yml").read_text(encoding="utf-8")
assert "docs_dir: docs" in content wiki_config = (cwd / "docs" / "mkdocs.wiki.yml").read_text(encoding="utf-8")
assert "Home: wiki/index.md" in content
# Wiki home replaces the lib home entry assert "docs_dir: lib" in lib_config
assert "Home: lib/testpkg/index.md" not in content 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( def test_wiki_without_module_flag_ok(
@@ -122,8 +125,6 @@ def test_missing_wiki_dir_errors(cli_runner, mock_mkdocs_build):
"S", "S",
"--wiki-dir", "--wiki-dir",
"docs/wiki", "docs/wiki",
"--mkdocs-yml",
"mkdocs.yml",
], ],
) )

View File

@@ -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