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/
docs/_site/
docs/build/
mkdocs.yml
# =========================
# MkDocs / Sphinx output

View File

@@ -23,41 +23,49 @@ pip install doc-forge
# CLI usage
## Generate an MkDocs site from a Python package:
Each site kind (`lib`, `api`, `wiki`) is built independently into `site/{kind}`.
## Build the library reference from a Python package:
```bash
doc-forge build --mkdocs --module my_package
```
## Build the API reference from an OpenAPI spec:
```bash
doc-forge build --api --openapi-spec spec.json
```
## Build the hand-written wiki:
```bash
doc-forge build --wiki --site-name my_package
```
## Generate MCP JSON documentation:
```bash
doc-forge build --mcp --module my_package
```
## Generate MkDocs site and MCP JSON documentation:
## Build several kinds in one pass:
```bash
doc-forge build --mcp --mkdocs --module my_package
doc-forge build --mcp --mkdocs --wiki --module my_package
```
## Include a hand-written wiki in the MkDocs site:
Each enabled kind gets its own MkDocs config (`docs/mkdocs.{lib,api,wiki}.yml`)
and its own site under `site/`.
## Serve a site locally:
```bash
doc-forge build --wiki --mkdocs --module my_package
```
## Build wiki pages only (no module required):
```bash
doc-forge build --wiki --site-name my_package
```
## Serve MkDocs locally:
```bash
doc-forge serve --mkdocs --module my_package
doc-forge serve --wiki # preview from docs/mkdocs.wiki.yml
doc-forge serve --lib
doc-forge serve --api
# or any config directly:
doc-forge serve --mkdocs --mkdocs-yml docs/mkdocs.wiki.yml
```
## Serve MCP locally:

View File

@@ -22,41 +22,49 @@ pip install doc-forge
# CLI usage
## Generate an MkDocs site from a Python package:
Each site kind (`lib`, `api`, `wiki`) is built independently into `site/{kind}`.
## Build the library reference from a Python package:
```bash
doc-forge build --mkdocs --module my_package
```
## Build the API reference from an OpenAPI spec:
```bash
doc-forge build --api --openapi-spec spec.json
```
## Build the hand-written wiki:
```bash
doc-forge build --wiki --site-name my_package
```
## Generate MCP JSON documentation:
```bash
doc-forge build --mcp --module my_package
```
## Generate MkDocs site and MCP JSON documentation:
## Build several kinds in one pass:
```bash
doc-forge build --mcp --mkdocs --module my_package
doc-forge build --mcp --mkdocs --wiki --module my_package
```
## Include a hand-written wiki in the MkDocs site:
Each enabled kind gets its own MkDocs config (`docs/mkdocs.{lib,api,wiki}.yml`)
and its own site under `site/`.
## Serve a site locally:
```bash
doc-forge build --wiki --mkdocs --module my_package
```
## Build wiki pages only (no module required):
```bash
doc-forge build --wiki --site-name my_package
```
## Serve MkDocs locally:
```bash
doc-forge serve --mkdocs --module my_package
doc-forge serve --wiki # preview from docs/mkdocs.wiki.yml
doc-forge serve --lib
doc-forge serve --api
# or any config directly:
doc-forge serve --mkdocs --mkdocs-yml docs/mkdocs.wiki.yml
```
## Serve MCP locally:

View File

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

View File

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

View File

@@ -6,17 +6,19 @@ Utilities for working with MkDocs in the doc-forge CLI.
---
Notes:
- A single generated `mkdocs.yml` serves lib, api, and wiki content with
merged navigation. Wiki navigation, when enabled, precedes every other
group and its `index.md` becomes the site `Home`.
- A separate `mkdocs.{kind}.yml` configuration and build is emitted per
enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and
written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).
- Navigation blocks are re-rooted per kind: the wiki navigation drops its
leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.
---
"""
import os
from collections.abc import Iterable
from importlib import resources
from pathlib import Path
from typing import Any, cast
import click
import yaml
@@ -83,53 +85,171 @@ def generate_sources(
)
def generate_config(
docs_dir: Path,
def build_lib_nav(
nav_file: Path,
template: Path | None,
out: Path,
site_name: str,
modes: Iterable[str] | None = None,
site_description: str | None = None,
site_author: str | None = None,
wiki_dir: Path | None = None,
) -> None:
docs_root: Path,
) -> tuple[list[dict[str, Any]], dict[str, str] | None]:
"""
Generate an `mkdocs.yml` configuration file.
Build the re-rooted navigation block for a lib site.
The configuration is created by combining a template configuration
with a navigation structure derived from the docforge navigation
specification (and, when a wiki directory is provided, from the wiki
file structure).
The ``docs_dir`` is always written relative to the MkDocs root and is
expected to be the shared documentation parent (for example ``docs``),
with generated sources nested under ``lib/`` or ``api/`` subdirectories
and hand-written wiki content under a ``wiki/`` subdirectory.
The navigation specification is resolved against the shared documentation
root and every resulting path is re-rooted relative to the ``lib``
subdirectory by stripping its leading ``lib/`` scope component.
Args:
docs_dir (Path):
Shared documentation root used as the MkDocs ``docs_dir``.
nav_file (Path):
Path to the `docforge.nav.yml` navigation specification.
template (Path | None):
Optional path to a fully custom MkDocs configuration template.
If not provided, built-in templates are merged; the provided
template replaces the built-in templates entirely.
docs_root (Path):
Shared documentation root containing the ``lib`` sources.
Returns:
tuple[list[dict[str, Any]], dict[str, str] | None]:
The re-rooted navigation block and the optional theme icon
mapping from the specification.
Raises:
click.FileError:
If the navigation specification cannot be found.
"""
if not nav_file.exists():
raise click.FileError(str(nav_file), hint="Nav spec not found")
spec = load_nav_spec(nav_file)
resolved = resolve_nav(spec, docs_root)
block = MkDocsNavEmitter().emit(resolved)
return cast(list[dict[str, Any]], _strip_scope(block, "lib")), spec.icon
def build_wiki_nav_block(wiki_dir: Path) -> list[dict[str, Any]]:
"""
Build the re-rooted navigation block for a wiki site.
The wiki navigation derived from the wiki file structure is re-rooted
relative to the wiki directory itself by stripping the leading ``wiki/``
scope component.
Args:
wiki_dir (Path):
Path to the hand-written wiki directory, for example ``docs/wiki``.
Returns:
list[dict[str, Any]]:
Navigation entries relative to the wiki directory.
Raises:
click.FileError:
If the wiki directory does not exist.
"""
if not wiki_dir.exists():
raise click.FileError(str(wiki_dir), hint="Wiki dir not found")
return cast(list[dict[str, Any]], _strip_scope(build_wiki_nav(wiki_dir), "wiki"))
def load_spec_icon(nav_file: Path) -> dict[str, str] | None:
"""
Load the theme icon mapping from a navigation specification.
Args:
nav_file (Path):
Path to the navigation specification file.
Returns:
dict[str, str] | None:
The icon mapping, or ``None`` when the specification file is
absent or cannot be parsed.
"""
if not nav_file.exists():
return None
try:
return load_nav_spec(nav_file).icon
except (ValueError, yaml.YAMLError):
return None
def _strip_scope(value: object, scope: str) -> object:
"""
Re-root navigation paths by removing a leading scope component.
The navigation block is walked recursively and every relative path
string that starts with ``{scope}/`` has that prefix removed, so a lib
or wiki site scoped to its own ``docs_dir`` can reuse paths that were
originally written relative to the shared documentation root.
Args:
value (object):
Navigation block, group list, path string, or scalar value.
scope (str):
Leading path component to strip, for example ``lib`` or ``wiki``.
Returns:
object:
The navigation structure with re-rooted paths.
"""
if isinstance(value, str):
prefix = f"{scope}/"
return value[len(prefix) :] if value.startswith(prefix) else value
if isinstance(value, list):
return [_strip_scope(item, scope) for item in value]
if isinstance(value, dict):
return {key: _strip_scope(val, scope) for key, val in value.items()}
return value
def generate_site_config(
kind: str,
kind_root: Path,
nav_block: list[dict[str, Any]],
out: Path,
site_name: str,
docs_dir: str,
site_dir: str,
template: Path | None = None,
site_description: str | None = None,
site_author: str | None = None,
theme_icon: dict[str, str] | None = None,
) -> None:
"""
Generate a per-kind `mkdocs.{kind}.yml` configuration file.
The configuration is created by merging the shared ``mkdocs.common.yml``
template with the fragment contributed by the kind (``lib``, ``api``, or
``wiki``). Both ``docs_dir`` and ``site_dir`` are written relative to the
configuration file's directory: the kind's sources when expressed as a
sibling path (for example ``lib``) and the per-kind site output (for
example ``../site/lib``).
Args:
kind (str):
Documentation kind, one of ``lib``, ``api``, or ``wiki``.
kind_root (Path):
Directory scoped to the kind (for example ``docs/lib``) that
serves as the MkDocs ``docs_dir``.
nav_block (list[dict[str, Any]]):
Re-rooted navigation entries for the kind's site.
out (Path):
Destination path where the generated `mkdocs.yml` file will be written.
Destination path where the generated ``mkdocs.{kind}.yml`` file
is written.
site_name (str):
Display name for the generated documentation site.
modes (Iterable[str] | None):
Documentation modes to enable. Each mode contributes its own
built-in template fragment (for example ``lib``, ``api``, or
``wiki``), merged on top of the shared ``mkdocs.common.yml``
template.
docs_dir (str):
MkDocs ``docs_dir`` value, relative to the configuration
file's directory.
site_dir (str):
MkDocs ``site_dir`` value, relative to the configuration
file's directory.
template (Path | None):
Optional path to a fully custom MkDocs configuration template
that replaces the built-in templates entirely.
site_description (str | None):
Optional site description written into the configuration.
@@ -137,50 +257,26 @@ def generate_config(
site_author (str | None):
Optional site author written into the configuration.
wiki_dir (Path | None):
Optional path to a hand-written wiki directory (for example
``docs/wiki``). When provided, the site navigation is derived
from the wiki file structure and placed before the navigation
groups defined in ``nav_file``.
Raises:
click.FileError:
If the navigation specification, template, or wiki directory
cannot be found.
theme_icon (dict[str, str] | None):
Optional mapping of theme icon entries injected as
``theme.icon``.
"""
if not nav_file.exists() and wiki_dir is None:
raise click.FileError(str(nav_file), hint="Nav spec not found")
nav_block: list[dict] = []
if nav_file.exists():
spec = load_nav_spec(nav_file)
resolved = resolve_nav(spec, docs_dir)
nav_block = MkDocsNavEmitter().emit(resolved)
else:
spec = None
if wiki_dir is not None:
if not wiki_dir.exists():
raise click.FileError(str(wiki_dir), hint="Wiki dir not found")
wiki_nav = build_wiki_nav(wiki_dir)
if wiki_nav:
nav_block = wiki_nav + [entry for entry in nav_block if "Home" not in entry]
data = _load_template(template, modes)
data = _load_template(template, [kind])
data["site_name"] = site_name
if site_description:
data["site_description"] = site_description
if site_author:
data["site_author"] = site_author
data["docs_dir"] = Path(os.path.relpath(docs_dir, out.parent)).as_posix()
data["docs_dir"] = docs_dir
data["site_dir"] = site_dir
data["nav"] = nav_block
if spec is not None and spec.icon:
if theme_icon:
theme = data.setdefault("theme", {})
if not isinstance(theme, dict):
theme = {}
theme["icon"] = spec.icon
theme["icon"] = theme_icon
data["theme"] = theme
out.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8")
@@ -310,28 +406,28 @@ def _deep_merge(base: dict, part: dict) -> dict:
return base
def build(mkdocs_yml: Path) -> None:
def build_configs(yml_paths: Iterable[Path]) -> None:
"""
Build the MkDocs documentation site.
Build the MkDocs documentation site for every given configuration.
This function loads the MkDocs configuration and runs the MkDocs
build command to generate the final static documentation site.
Each configuration file is loaded and built in turn, producing the
per-kind static sites (``site/lib``, ``site/api``, ``site/wiki``).
Args:
mkdocs_yml (Path):
Path to the `mkdocs.yml` configuration file.
yml_paths (Iterable[Path]):
Configuration files to build, in order.
Raises:
click.ClickException:
If the configuration file does not exist.
If a configuration file does not exist.
"""
if not mkdocs_yml.exists():
raise click.ClickException(f"mkdocs.yml not found: {mkdocs_yml}")
from mkdocs.commands.build import build as mkdocs_build
from mkdocs.config import load_config
mkdocs_build(load_config(str(mkdocs_yml)))
for yml in yml_paths:
if not yml.exists():
raise click.ClickException(f"mkdocs.yml not found: {yml}")
mkdocs_build(load_config(str(yml)))
def serve(mkdocs_yml: Path) -> None:

View File

@@ -1,5 +1,6 @@
from collections.abc import Iterable
from pathlib import Path
from typing import Any
def generate_sources(
module: str,
@@ -8,16 +9,25 @@ def generate_sources(
module_is_source: bool | None = None,
readme_dir: Path | None = None,
) -> None: ...
def generate_config(
docs_dir: Path,
def build_lib_nav(
nav_file: Path,
template: Path | None,
docs_root: Path,
) -> tuple[list[dict[str, Any]], dict[str, str] | None]: ...
def build_wiki_nav_block(wiki_dir: Path) -> list[dict[str, Any]]: ...
def load_spec_icon(nav_file: Path) -> dict[str, str] | None: ...
def _strip_scope(value: object, scope: str) -> object: ...
def generate_site_config(
kind: str,
kind_root: Path,
nav_block: list[dict[str, Any]],
out: Path,
site_name: str,
modes: Iterable[str] | None = None,
docs_dir: str,
site_dir: str,
template: Path | None = None,
site_description: str | None = None,
site_author: str | None = None,
wiki_dir: Path | None = None,
theme_icon: dict[str, str] | None = None,
) -> None: ...
def build(mkdocs_yml: Path) -> None: ...
def build_configs(yml_paths: Iterable[Path]) -> None: ...
def serve(mkdocs_yml: Path) -> None: ...

View File

@@ -2,8 +2,15 @@
"module": "docforge.cli.commands",
"content": {
"path": "docforge.cli.commands",
"docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` share a single MkDocs build; `--mcp`\n generates a machine-readable bundle independently.\n\n---",
"docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` each emit their own MkDocs config and\n build (`docs/mkdocs.{kind}.yml` into `site/{kind}`); `--mcp` generates a\n machine-readable bundle independently.\n\n---",
"objects": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.commands.os",
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
"docstring": null
},
"Path": {
"name": "Path",
"kind": "alias",
@@ -245,15 +252,8 @@
"kind": "module",
"path": "docforge.cli.commands.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": {
"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": {
"name": "Iterable",
"kind": "alias",
@@ -275,6 +275,20 @@
"signature": "<bound method Alias.signature of Alias('Path', 'docforge.cli.mkdocs_utils.Path')>",
"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": {
"name": "click",
"kind": "alias",
@@ -393,19 +407,40 @@
"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``."
},
"generate_config": {
"name": "generate_config",
"build_lib_nav": {
"name": "build_lib_nav",
"kind": "function",
"path": "docforge.cli.commands.mkdocs_utils.generate_config",
"signature": "<bound method Alias.signature of Alias('generate_config', 'docforge.cli.mkdocs_utils.generate_config')>",
"docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found."
"path": "docforge.cli.commands.mkdocs_utils.build_lib_nav",
"signature": "<bound method Alias.signature of Alias('build_lib_nav', 'docforge.cli.mkdocs_utils.build_lib_nav')>",
"docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found."
},
"build": {
"name": "build",
"build_wiki_nav_block": {
"name": "build_wiki_nav_block",
"kind": "function",
"path": "docforge.cli.commands.mkdocs_utils.build",
"signature": "<bound method Alias.signature of Alias('build', 'docforge.cli.mkdocs_utils.build')>",
"docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist."
"path": "docforge.cli.commands.mkdocs_utils.build_wiki_nav_block",
"signature": "<bound method Alias.signature of Alias('build_wiki_nav_block', 'docforge.cli.mkdocs_utils.build_wiki_nav_block')>",
"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": {
"name": "serve",
@@ -522,21 +557,21 @@
"name": "build",
"kind": "function",
"path": "docforge.cli.commands.build",
"signature": "<bound method Function.signature of Function('build', 38, 260)>",
"docstring": "Build documentation artifacts.\n\nThis command runs the full documentation pipeline: it loads Python\nmodules, generates renderer-specific documentation sources, and\noptionally builds or serves the final output.\n\nDepending on the selected options, the build can target:\n\n- MkDocs static documentation sites for library reference docs\n- Swagger-enabled API docs generated from an OpenAPI spec\n- Hand-written wiki pages included in the MkDocs site\n- MCP structured documentation resources\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` are combined into a single MkDocs\n build, while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable MkDocs library documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Include a hand-written wiki directory in the MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the MkDocs site.\n\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n mkdocs_yml (Path):\n Output path for the generated MkDocs configuration.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting."
"signature": "<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 the final output.\n\nDepending on the selected options, the build can target:\n\n- A lib MkDocs site (`--mkdocs`) for library reference docs\n- A swagger-enabled API MkDocs site (`--api`) built from an OpenAPI spec\n- A wiki MkDocs site (`--wiki`) built from hand-written markdown\n- MCP structured documentation resources (`--mcp`)\n\nEach enabled site kind produces its own MkDocs configuration\n(`docs/mkdocs.{kind}.yml`) and its own build (`site/{kind}`).\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` emit independent MkDocs builds,\n while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable the lib MkDocs documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Build a hand-written wiki directory as its own MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the lib and wiki MkDocs sites.\n\n docs_dir (Path):\n Shared documentation root used for generated sources.\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting."
},
"serve": {
"name": "serve",
"kind": "function",
"path": "docforge.cli.commands.serve",
"signature": "<bound method Function.signature of Function('serve', 263, 324)>",
"docstring": "Serve generated documentation locally.\n\nDepending on the selected mode, this command starts either:\n\n- A MkDocs development server for browsing documentation\n- An MCP server exposing structured documentation resources\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided."
"signature": "<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 a site, or\n- An MCP server exposing structured documentation resources\n\nThe kind flags (`--lib`, `--api`, `--wiki`) select the generated\nper-kind config (`docs/mkdocs.{kind}.yml`); `--mkdocs` serves the config\npassed via `--mkdocs-yml`.\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site from ``--mkdocs-yml``.\n\n lib (bool):\n Serve the lib MkDocs site.\n\n api (bool):\n Serve the API MkDocs site.\n\n wiki (bool):\n Serve the wiki MkDocs site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided."
},
"tree": {
"name": "tree",
"kind": "function",
"path": "docforge.cli.commands.tree",
"signature": "<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."
},
"Group": {

View File

@@ -134,8 +134,15 @@
"kind": "module",
"path": "docforge.cli.commands",
"signature": null,
"docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` share a single MkDocs build; `--mcp`\n generates a machine-readable bundle independently.\n\n---",
"docstring": "# Summary\n\nCommand definitions for the doc-forge CLI.\n\nProvides the CLI structure using Click, including build, serve, and tree commands.\n\n---\n\nNotes:\n - The `build` command validates requested modes before generating anything.\n - `--mkdocs`, `--api`, and `--wiki` each emit their own MkDocs config and\n build (`docs/mkdocs.{kind}.yml` into `site/{kind}`); `--mcp` generates a\n machine-readable bundle independently.\n\n---",
"members": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.commands.os",
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
"docstring": null
},
"Path": {
"name": "Path",
"kind": "alias",
@@ -377,15 +384,8 @@
"kind": "module",
"path": "docforge.cli.commands.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": {
"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": {
"name": "Iterable",
"kind": "alias",
@@ -407,6 +407,20 @@
"signature": "<bound method Alias.signature of Alias('Path', 'docforge.cli.mkdocs_utils.Path')>",
"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": {
"name": "click",
"kind": "alias",
@@ -525,19 +539,40 @@
"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``."
},
"generate_config": {
"name": "generate_config",
"build_lib_nav": {
"name": "build_lib_nav",
"kind": "function",
"path": "docforge.cli.commands.mkdocs_utils.generate_config",
"signature": "<bound method Alias.signature of Alias('generate_config', 'docforge.cli.mkdocs_utils.generate_config')>",
"docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found."
"path": "docforge.cli.commands.mkdocs_utils.build_lib_nav",
"signature": "<bound method Alias.signature of Alias('build_lib_nav', 'docforge.cli.mkdocs_utils.build_lib_nav')>",
"docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found."
},
"build": {
"name": "build",
"build_wiki_nav_block": {
"name": "build_wiki_nav_block",
"kind": "function",
"path": "docforge.cli.commands.mkdocs_utils.build",
"signature": "<bound method Alias.signature of Alias('build', 'docforge.cli.mkdocs_utils.build')>",
"docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist."
"path": "docforge.cli.commands.mkdocs_utils.build_wiki_nav_block",
"signature": "<bound method Alias.signature of Alias('build_wiki_nav_block', 'docforge.cli.mkdocs_utils.build_wiki_nav_block')>",
"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": {
"name": "serve",
@@ -654,21 +689,21 @@
"name": "build",
"kind": "function",
"path": "docforge.cli.commands.build",
"signature": "<bound method Function.signature of Function('build', 38, 260)>",
"docstring": "Build documentation artifacts.\n\nThis command runs the full documentation pipeline: it loads Python\nmodules, generates renderer-specific documentation sources, and\noptionally builds or serves the final output.\n\nDepending on the selected options, the build can target:\n\n- MkDocs static documentation sites for library reference docs\n- Swagger-enabled API docs generated from an OpenAPI spec\n- Hand-written wiki pages included in the MkDocs site\n- MCP structured documentation resources\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` are combined into a single MkDocs\n build, while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable MkDocs library documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Include a hand-written wiki directory in the MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the MkDocs site.\n\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n mkdocs_yml (Path):\n Output path for the generated MkDocs configuration.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting."
"signature": "<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 the final output.\n\nDepending on the selected options, the build can target:\n\n- A lib MkDocs site (`--mkdocs`) for library reference docs\n- A swagger-enabled API MkDocs site (`--api`) built from an OpenAPI spec\n- A wiki MkDocs site (`--wiki`) built from hand-written markdown\n- MCP structured documentation resources (`--mcp`)\n\nEach enabled site kind produces its own MkDocs configuration\n(`docs/mkdocs.{kind}.yml`) and its own build (`site/{kind}`).\n\nNotes:\n - At least one of `--mcp`, `--mkdocs`, `--wiki`, or `--api` must be\n provided.\n - `--mkdocs`, `--api`, and `--wiki` emit independent MkDocs builds,\n while `--mcp` emits a machine-readable bundle.\n\nArgs:\n mcp (bool):\n Enable MCP documentation generation.\n\n mkdocs (bool):\n Enable the lib MkDocs documentation generation.\n\n api (bool):\n Enable API documentation generation from an OpenAPI spec.\n\n wiki (bool):\n Build a hand-written wiki directory as its own MkDocs site.\n\n module_is_source (bool):\n Treat the specified module directory as the project root.\n\n module (str | None):\n Python module import path to document.\n\n openapi_spec (Path | None):\n Path to the OpenAPI JSON specification used for API docs.\n\n project_name (str | None):\n Optional override for the project name.\n\n site_name (str | None):\n Display name for the lib and wiki MkDocs sites.\n\n docs_dir (Path):\n Shared documentation root used for generated sources.\n wiki_dir (Path):\n Directory containing hand-written wiki markdown files.\n\n nav_file (Path):\n Path to the navigation specification file.\n\n template (Path | None):\n Optional custom MkDocs configuration template.\n\n out_dir (Path):\n Output directory for generated MCP resources.\n\nRaises:\n click.UsageError:\n If required options are missing or conflicting."
},
"serve": {
"name": "serve",
"kind": "function",
"path": "docforge.cli.commands.serve",
"signature": "<bound method Function.signature of Function('serve', 263, 324)>",
"docstring": "Serve generated documentation locally.\n\nDepending on the selected mode, this command starts either:\n\n- A MkDocs development server for browsing documentation\n- An MCP server exposing structured documentation resources\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided."
"signature": "<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 a site, or\n- An MCP server exposing structured documentation resources\n\nThe kind flags (`--lib`, `--api`, `--wiki`) select the generated\nper-kind config (`docs/mkdocs.{kind}.yml`); `--mkdocs` serves the config\npassed via `--mkdocs-yml`.\n\nArgs:\n mcp (bool):\n Serve documentation using the MCP server.\n\n mkdocs (bool):\n Serve the MkDocs development site from ``--mkdocs-yml``.\n\n lib (bool):\n Serve the lib MkDocs site.\n\n api (bool):\n Serve the API MkDocs site.\n\n wiki (bool):\n Serve the wiki MkDocs site.\n\n module (str | None):\n Python module import path to serve via MCP.\n\n mkdocs_yml (Path):\n Path to the MkDocs configuration file.\n\n out_dir (Path):\n Root directory containing MCP documentation resources.\n\nRaises:\n click.UsageError:\n If invalid or conflicting options are provided."
},
"tree": {
"name": "tree",
"kind": "function",
"path": "docforge.cli.commands.tree",
"signature": "<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."
},
"Group": {
@@ -805,15 +840,8 @@
"kind": "module",
"path": "docforge.cli.mkdocs_utils",
"signature": null,
"docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A single generated `mkdocs.yml` serves lib, api, and wiki content with\n merged navigation. Wiki navigation, when enabled, precedes every other\n group and its `index.md` becomes the site `Home`.\n\n---",
"docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A separate `mkdocs.{kind}.yml` configuration and build is emitted per\n enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and\n written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).\n - Navigation blocks are re-rooted per kind: the wiki navigation drops its\n leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.\n\n---",
"members": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.mkdocs_utils.os",
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
"docstring": null
},
"Iterable": {
"name": "Iterable",
"kind": "alias",
@@ -835,6 +863,20 @@
"signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>",
"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": {
"name": "click",
"kind": "alias",
@@ -950,28 +992,49 @@
"name": "generate_sources",
"kind": "function",
"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``."
},
"generate_config": {
"name": "generate_config",
"build_lib_nav": {
"name": "build_lib_nav",
"kind": "function",
"path": "docforge.cli.mkdocs_utils.generate_config",
"signature": "<bound method Function.signature of Function('generate_config', 86, 186)>",
"docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found."
"path": "docforge.cli.mkdocs_utils.build_lib_nav",
"signature": "<bound method Function.signature of Function('build_lib_nav', 88, 121)>",
"docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found."
},
"build": {
"name": "build",
"build_wiki_nav_block": {
"name": "build_wiki_nav_block",
"kind": "function",
"path": "docforge.cli.mkdocs_utils.build",
"signature": "<bound method Function.signature of Function('build', 313, 334)>",
"docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist."
"path": "docforge.cli.mkdocs_utils.build_wiki_nav_block",
"signature": "<bound method Function.signature of Function('build_wiki_nav_block', 124, 147)>",
"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": {
"name": "serve",
"kind": "function",
"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."
}
}

View File

@@ -2,15 +2,8 @@
"module": "docforge.cli.mkdocs_utils",
"content": {
"path": "docforge.cli.mkdocs_utils",
"docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A single generated `mkdocs.yml` serves lib, api, and wiki content with\n merged navigation. Wiki navigation, when enabled, precedes every other\n group and its `index.md` becomes the site `Home`.\n\n---",
"docstring": "# Summary\n\nUtilities for working with MkDocs in the doc-forge CLI.\n\n---\n\nNotes:\n - A separate `mkdocs.{kind}.yml` configuration and build is emitted per\n enabled kind (lib, api, wiki), each scoped to its own `docs_dir` and\n written into its own `site_dir` (`site/lib`, `site/api`, `site/wiki`).\n - Navigation blocks are re-rooted per kind: the wiki navigation drops its\n leading `wiki/` scope and the resolved nav spec drops its `lib/` scope.\n\n---",
"objects": {
"os": {
"name": "os",
"kind": "alias",
"path": "docforge.cli.mkdocs_utils.os",
"signature": "<bound method Alias.signature of Alias('os', 'os')>",
"docstring": null
},
"Iterable": {
"name": "Iterable",
"kind": "alias",
@@ -32,6 +25,20 @@
"signature": "<bound method Alias.signature of Alias('Path', 'pathlib.Path')>",
"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": {
"name": "click",
"kind": "alias",
@@ -147,28 +154,49 @@
"name": "generate_sources",
"kind": "function",
"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``."
},
"generate_config": {
"name": "generate_config",
"build_lib_nav": {
"name": "build_lib_nav",
"kind": "function",
"path": "docforge.cli.mkdocs_utils.generate_config",
"signature": "<bound method Function.signature of Function('generate_config', 86, 186)>",
"docstring": "Generate an `mkdocs.yml` configuration file.\n\nThe configuration is created by combining a template configuration\nwith a navigation structure derived from the docforge navigation\nspecification (and, when a wiki directory is provided, from the wiki\nfile structure).\n\nThe ``docs_dir`` is always written relative to the MkDocs root and is\nexpected to be the shared documentation parent (for example ``docs``),\nwith generated sources nested under ``lib/`` or ``api/`` subdirectories\nand hand-written wiki content under a ``wiki/`` subdirectory.\n\nArgs:\n docs_dir (Path):\n Shared documentation root used as the MkDocs ``docs_dir``.\n\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n template (Path | None):\n Optional path to a fully custom MkDocs configuration template.\n If not provided, built-in templates are merged; the provided\n template replaces the built-in templates entirely.\n\n out (Path):\n Destination path where the generated `mkdocs.yml` file will be written.\n\n site_name (str):\n Display name for the generated documentation site.\n\n modes (Iterable[str] | None):\n Documentation modes to enable. Each mode contributes its own\n built-in template fragment (for example ``lib``, ``api``, or\n ``wiki``), merged on top of the shared ``mkdocs.common.yml``\n template.\n\n site_description (str | None):\n Optional site description written into the configuration.\n\n site_author (str | None):\n Optional site author written into the configuration.\n\n wiki_dir (Path | None):\n Optional path to a hand-written wiki directory (for example\n ``docs/wiki``). When provided, the site navigation is derived\n from the wiki file structure and placed before the navigation\n groups defined in ``nav_file``.\n\nRaises:\n click.FileError:\n If the navigation specification, template, or wiki directory\n cannot be found."
"path": "docforge.cli.mkdocs_utils.build_lib_nav",
"signature": "<bound method Function.signature of Function('build_lib_nav', 88, 121)>",
"docstring": "Build the re-rooted navigation block for a lib site.\n\nThe navigation specification is resolved against the shared documentation\nroot and every resulting path is re-rooted relative to the ``lib``\nsubdirectory by stripping its leading ``lib/`` scope component.\n\nArgs:\n nav_file (Path):\n Path to the `docforge.nav.yml` navigation specification.\n\n docs_root (Path):\n Shared documentation root containing the ``lib`` sources.\n\nReturns:\n tuple[list[dict[str, Any]], dict[str, str] | None]:\n The re-rooted navigation block and the optional theme icon\n mapping from the specification.\n\nRaises:\n click.FileError:\n If the navigation specification cannot be found."
},
"build": {
"name": "build",
"build_wiki_nav_block": {
"name": "build_wiki_nav_block",
"kind": "function",
"path": "docforge.cli.mkdocs_utils.build",
"signature": "<bound method Function.signature of Function('build', 313, 334)>",
"docstring": "Build the MkDocs documentation site.\n\nThis function loads the MkDocs configuration and runs the MkDocs\nbuild command to generate the final static documentation site.\n\nArgs:\n mkdocs_yml (Path):\n Path to the `mkdocs.yml` configuration file.\n\nRaises:\n click.ClickException:\n If the configuration file does not exist."
"path": "docforge.cli.mkdocs_utils.build_wiki_nav_block",
"signature": "<bound method Function.signature of Function('build_wiki_nav_block', 124, 147)>",
"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": {
"name": "serve",
"kind": "function",
"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."
}
}

File diff suppressed because one or more lines are too long

View File

@@ -67,40 +67,32 @@ plugins:
show_object_full_path: false
show_symbol_type_heading: true
site_name: docforge
docs_dir: docs
docs_dir: lib
site_dir: ../site/lib
nav:
- Home: wiki/index.md
- Overview: wiki/01_overview.md
- Architecture: wiki/02_architecture.md
- Conventions: wiki/03_conventions.md
- Iterative Workflow: wiki/04_iterative_workflow.md
- Development:
- Environment: wiki/05_development/01_environment.md
- Quality Gates: wiki/05_development/02_quality_gates.md
- Gsdfc Guide: wiki/05_development/03_gsdfc_guide.md
- Mcp Guide: wiki/05_development/04_mcp_guide.md
- Home: index.md
- Loaders:
- lib/loaders/index.md
- lib/loaders/griffe_loader.md
- loaders/index.md
- loaders/griffe_loader.md
- Models:
- lib/models/index.md
- lib/models/module.md
- lib/models/object.md
- lib/models/project.md
- models/index.md
- models/module.md
- models/object.md
- models/project.md
- Navigation:
- lib/nav/index.md
- lib/nav/spec.md
- lib/nav/resolver.md
- lib/nav/mkdocs.md
- nav/index.md
- nav/spec.md
- nav/resolver.md
- nav/mkdocs.md
- Renderers:
- lib/renderers/index.md
- lib/renderers/base.md
- lib/renderers/mkdocs_renderer.md
- lib/renderers/mcp_renderer.md
- renderers/index.md
- renderers/base.md
- renderers/mkdocs_renderer.md
- renderers/mcp_renderer.md
- CLI:
- lib/cli/index.md
- lib/cli/main.md
- lib/cli/commands.md
- lib/cli/api_utils.md
- lib/cli/mcp_utils.md
- lib/cli/mkdocs_utils.md
- cli/index.md
- cli/main.md
- cli/commands.md
- cli/api_utils.md
- cli/mcp_utils.md
- cli/mkdocs_utils.md

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

View File

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

View File

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

View File

@@ -2,7 +2,7 @@
`docforge` is an internal documentation tool that generates reference
documentation from Google-Styled Doc-Forge Convention (GSDFC) docstrings and
assembles it into a single MkDocs site, alongside hand-written wiki pages and
assembles it into per-kind MkDocs sites, alongside hand-written wiki pages and
OpenAPI-based API docs.
---
@@ -11,7 +11,7 @@ OpenAPI-based API docs.
- 🧪 GSDFC docstring extraction via Griffe
- 🧭 Auto-derived navigation for hand-written wiki pages
- 🗂 Combined MkDocs build: wiki first, then generated library/API reference
- 🗂 Independent MkDocs builds per kind (lib, api, wiki) into `site/{kind}`
- 🔌 Material MkDocs theming out of the box
- 📦 MCP structured documentation resources

View File

@@ -1,4 +1,4 @@
import json
import json
from pathlib import Path
from docforge.cli.main import cli
@@ -55,8 +55,10 @@ def test_api_build_full_flow(
index = (api_dir / "index.md").read_text(encoding="utf-8")
assert '<swagger-ui src="openapi.json"/>' in index
config = (cwd / "mkdocs.yml").read_text(encoding="utf-8")
assert "docs_dir: docs" in config
config = (cwd / "docs" / "mkdocs.api.yml").read_text(encoding="utf-8")
assert "docs_dir: api" in config
assert "site_dir: ../site/api" in config
assert "- API Reference: index.md" in config
assert "site_name: Aetoskia Auth Server" in config
assert "site_description: Auth docs" in config
assert "site_author: Aetoskia Dev Team" in config
@@ -90,10 +92,9 @@ def test_api_build_rejects_site_name_override(cli_runner):
assert "cannot be overridden" in result.output
def test_api_build_combined_with_mkdocs_allows_site_name_for_lib(
def test_api_build_combined_with_mkdocs_emits_separate_configs(
cli_runner, mock_mkdocs_build, mock_mkdocs_load_config
):
# site_name is accepted when --mkdocs is also present (lib mode owns it)
with cli_runner.isolated_filesystem():
cwd = Path.cwd()
@@ -105,13 +106,7 @@ def test_api_build_combined_with_mkdocs_allows_site_name_for_lib(
spec_path = _write_spec(cwd)
nav_file = cwd / "docforge.nav.yml"
nav_file.write_text(
"home: lib/testpkg/index.md\n"
"groups:\n"
" API:\n"
" - api/index.md\n",
encoding="utf-8",
)
nav_file.write_text("home: lib/testpkg/index.md\ngroups: {}\n")
result = cli_runner.invoke(
cli,
@@ -128,7 +123,20 @@ def test_api_build_combined_with_mkdocs_allows_site_name_for_lib(
assert result.exit_code == 0, result.output
config = (cwd / "mkdocs.yml").read_text(encoding="utf-8")
assert "docs_dir: docs" in config
assert "swagger-ui-tag" in config
assert "mkdocstrings" in config
lib_config = cwd / "docs" / "mkdocs.lib.yml"
api_config = cwd / "docs" / "mkdocs.api.yml"
assert lib_config.exists()
assert api_config.exists()
lib_text = lib_config.read_text(encoding="utf-8")
assert "docs_dir: lib" in lib_text
assert "site_dir: ../site/lib" in lib_text
assert "Home: testpkg/index.md" in lib_text
api_text = api_config.read_text(encoding="utf-8")
assert "docs_dir: api" in api_text
assert "site_dir: ../site/api" in api_text
assert "- API Reference: index.md" in api_text
assert "site_name: Aetoskia Auth Server" in api_text
assert "swagger-ui-tag" in api_text
assert "mkdocstrings" in lib_text

View File

@@ -20,10 +20,6 @@ def test_mkdocs_build_full_flow(
nav_file = cwd / "docforge.nav.yml"
nav_file.write_text("home: lib/testpkg/index.md\ngroups: {}\n")
# We need to create a dummy testpkg/index.md for nav resolution if it's there
# But generate_sources will create it.
# Wait, the current logic runs generate_sources first, THEN generate_config.
result = cli_runner.invoke(
cli,
[
@@ -33,16 +29,19 @@ def test_mkdocs_build_full_flow(
"testpkg",
"--site-name",
"Test Site",
"--mkdocs-yml",
"mkdocs.yml",
],
)
assert result.exit_code == 0
assert mock_mkdocs_build() is True
assert (cwd / "mkdocs.yml").exists()
config = cwd / "docs" / "mkdocs.lib.yml"
assert config.exists()
assert (cwd / "docs" / "lib" / "testpkg" / "mod.md").exists()
assert "docs_dir: docs" in (cwd / "mkdocs.yml").read_text()
content = config.read_text(encoding="utf-8")
assert "docs_dir: lib" in content
assert "site_dir: ../site/lib" in content
assert "Home: testpkg/index.md" in content
def test_mkdocs_build_missing_module_fails(cli_runner):
@@ -66,7 +65,7 @@ def test_mkdocs_build_without_site_name_uses_module_as_default_full_flow(
(pkg / "__init__.py").write_text("")
(pkg / "mod.py").write_text("def f(): ...\n")
# Create nav spec expected by generate_config
# Create nav spec expected by build_lib_nav
nav_file = cwd / "docforge.nav.yml"
nav_file.write_text(
"home: lib/testpkg/index.md\ngroups: {}\n",
@@ -75,25 +74,18 @@ def test_mkdocs_build_without_site_name_uses_module_as_default_full_flow(
result = cli_runner.invoke(
cli,
[
"build",
"--mkdocs",
"--module",
"testpkg",
"--mkdocs-yml",
"mkdocs.yml",
],
["build", "--mkdocs", "--module", "testpkg"],
)
assert result.exit_code == 0
assert mock_mkdocs_build() is True
# MkDocs config must exist
mkdocs_yml = cwd / "mkdocs.yml"
assert mkdocs_yml.exists()
# The per-kind MkDocs config must exist
config = cwd / "docs" / "mkdocs.lib.yml"
assert config.exists()
# Site name must default to module name
content = mkdocs_yml.read_text()
content = config.read_text(encoding="utf-8")
assert "site_name: testpkg" in content
# Docs must be generated under the nested docs/lib dir

View File

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

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