Automated system for generating, decorating, and validating Mellea API documentation using Mintlify.
uv sync --all-extras --group dev # Same as CI — installs all extras + dev toolsNode.js LTS (v22 or earlier) is required for mintlify dev previews.
uv run poe apidocs # Generate + decorate (version auto-read from pyproject.toml)
uv run poe apidocs-preview # Generate fresh docs to /tmp and run quality audit
uv run poe apidocs-quality # Audit docstring quality (public symbols, no methods)
uv run poe apidocs-orphans # Find MDX files not referenced in docs.json navigation
uv run poe apidocs-validate # Verify coverage + MDX syntax
uv run poe apidocs-clean # Remove generated artefacts
uv run poe clidocs # Generate CLI reference page from Typer metadata
uv run poe clidocs-clean # Remove generated CLI reference pageThe apidocs task runs build.py, which orchestrates four steps: AST generation
(generate-ast.py), MDX decoration (decorate_api_mdx.py), nav rebuild, and CLI
reference generation (generate_cli_reference.py). Both the docs/docs/api/
directory and docs/docs/api-reference.mdx are fully generated artefacts —
do not commit or edit them by hand. docs/docs/reference/cli.md is also generated
— edit the CLI command docstrings in cli/ instead.
clidocs runs generate_cli_reference.py without validation flags — useful for
quick local iteration. The apidocs pipeline always passes --strict, which fails
the build if any command is missing required docstring sections or has options
without help= text. To test strict validation locally:
uv run python tooling/docs-autogen/generate_cli_reference.py --strictmellea/ source code
│
▼
[1] generate-ast.py
- Runs mdxify to extract classes, functions, docstrings
- Reorganises flat output into nested folder structure
- Strips empty files, updates frontmatter
- Generates docs/docs/api-reference.mdx (landing page)
- Writes API Reference tab into docs/docs/docs.json
│
▼
docs/docs/api/ (fresh copy, replaces previous entirely)
│
▼
[2] decorate_api_mdx.py
- Fix GitHub source links (version tag)
- Inject per-module preamble text
- Inject SidebarFix Mintlify component
- Escape { } in code blocks so MDX doesn't interpret them as JSX
- Add cross-reference links (e.g. `Backend` → link to its definition)
- Add CLASS/FUNC pills and visual dividers to headings
│
▼
[3] generate-ast.py --nav-only
- Re-reads decorated files to rebuild the landing page and nav
- Ensures API Reference tab cards show full module descriptions
│
▼
[4] generate_cli_reference.py (--strict in CI / apidocs; lenient via clidocs)
- Imports Typer app, walks Click command tree
- Extracts flags, types, defaults, help strings
- Parses docstring sections (Prerequisites, Output, Examples, See Also)
- Emits docs/docs/reference/cli.md (standard Markdown)
│
▼
[5] Mintlify dev server (mintlify dev from docs/docs/)
http://localhost:3000
── Optional tools (not part of the build pipeline) ──────────────────────────
validate.py (uv run poe apidocs-validate)
- GitHub source links correct?
- API coverage ≥ threshold?
- MDX syntax valid (no unescaped braces)?
- Internal cross-reference links resolve?
- No duplicate heading anchors?
tooling/docs-autogen/
├── README.md # This file
├── build.py # Unified wrapper: calls generate-ast then decorate
├── generate-ast.py # Step 1: MDX generation + nav + landing page
├── decorate_api_mdx.py # Step 2: decoration, escaping, cross-references
├── validate.py # Step 3: quality validation
├── audit_coverage.py # Symbol coverage + quality audit
├── generate_cli_reference.py # CLI reference page generator
├── test_cli_reference.py # Tests for CLI reference generation
├── test_escape_mdx.py # Tests for MDX brace escaping
├── test_cross_references.py
├── test_mintlify_anchors.py
├── test_anchor_collisions.py
└── test_validate.py
docs/docs/
├── docs.json # Mintlify config — API Reference tab auto-generated
├── api-reference.mdx # Auto-generated landing page
├── api/ # Fully generated — do not edit
│ └── mellea/
├── reference/
│ └── cli.md # Auto-generated CLI reference — do not edit
└── snippets/
└── SidebarFix.mdx # Mintlify sidebar component (hand-maintained)
Version is auto-detected from pyproject.toml. To override:
uv run python tooling/docs-autogen/build.py --version 0.4.0Cross-repo docs generation — generate docs for a different checkout without
touching that repo (e.g. ../mellea-b) by passing --source-dir:
# Build docs from mellea-b source, write output to /tmp/mellea-b-preview/api
uv run python tooling/docs-autogen/build.py \
--source-dir ../mellea-b \
--output-dir /tmp/mellea-b-preview/api
# Then audit the generated output against the same source
uv run python tooling/docs-autogen/audit_coverage.py \
--quality --no-methods \
--docs-dir /tmp/mellea-b-preview/api \
--source-dir ../mellea-b/melleaBoth build.py and audit_coverage.py accept --source-dir to point at a
different repo. generate-ast.py also accepts --source-dir if you need to
call it directly.
Coverage threshold (validate.py, default 80%):
uv run poe apidocs-validate # uses default threshold
uv run python tooling/docs-autogen/validate.py docs/docs/api --version 0.3.2 --coverage-threshold 50Docstring quality audit (audit_coverage.py --quality):
uv run poe apidocs-quality # missing, short, no Args/Returns/Raises (top-level, no methods)
# Extended options (direct invocation)
uv run python tooling/docs-autogen/audit_coverage.py \
--docs-dir docs/docs/api --quality # include class methods too
uv run python tooling/docs-autogen/audit_coverage.py \
--docs-dir docs/docs/api --quality --short-threshold 15 # raise "short" threshold
uv run python tooling/docs-autogen/audit_coverage.py \
--docs-dir docs/docs/api --quality --output report.json # save JSON report
uv run python tooling/docs-autogen/audit_coverage.py \
--docs-dir docs/docs/api --quality --fail-on-quality # exit 1 if any issues (CI/pre-commit)Eight issue kinds are reported:
| Kind | Flagged when |
|---|---|
missing |
No docstring at all |
short |
Fewer than --short-threshold words (default 5) |
no_args |
Function has parameters but no Args:/Parameters: section |
no_returns |
Function has a non-None return annotation but no Returns: section |
no_raises |
Function body contains raise statement but no Raises: section |
no_class_args |
Class __init__ has typed params but no Args: section |
no_attributes |
Class has public attributes but no Attributes: section |
param_mismatch |
Args: section documents parameter names not in the real signature |
*args / **kwargs forwarders — functions whose only non-self parameters are
*args and/or **kwargs are exempt from both no_args and param_mismatch. These
are variadic forwarders where the concrete signature carries no named parameters; the
docstring Args: section conventionally describes the accepted keyword arguments rather
than the signature itself. Authors should document those kwargs freely — the audit will
not flag them as mismatches.
The quality audit is informational — it does not fail the build unless --fail-on-quality
is passed. Use --output to track trends over time or feed results into issue tracking.
Navigation orphan audit (audit_coverage.py --orphans):
uv run poe apidocs-orphans # find MDX files not referenced in docs.json navigationReports any generated MDX files in docs/docs/api/ that are absent from the
navigation tree in docs/mint.json. Also works with --source-dir for
cross-repo audits.
Navigation and landing page are auto-generated by generate-ast.py.
Do not edit the API Reference tab in docs.json or api-reference.mdx by hand
— both are overwritten on every uv run poe apidocs run.
CLI reference generator (generate_cli_reference.py):
uv run poe clidocs # generate (lenient — for local iteration)
uv run python tooling/docs-autogen/generate_cli_reference.py --strict # strict mode (same as CI)
uv run python tooling/docs-autogen/generate_cli_reference.py --docs-root /tmp/preview # custom output location
uv run python tooling/docs-autogen/generate_cli_reference.py --source-dir ../mellea-b # cross-repo--strict exits non-zero if any command lacks a summary, Prerequisites:, or
Output: section, or has options without help= text. The apidocs pipeline
always runs with --strict; clidocs does not.
Skipping pipeline steps (build.py):
uv run python tooling/docs-autogen/build.py --skip-cli-reference # API docs only, no CLI reference
uv run python tooling/docs-autogen/build.py --skip-generation # decoration + CLI reference only
uv run python tooling/docs-autogen/build.py --skip-decoration # generation + CLI reference onlyFull clean — apidocs-clean removes only the API artefacts; clidocs-clean
removes only the CLI reference. To remove everything:
uv run poe apidocs-clean && uv run poe clidocs-cleandecorate_api_mdx.py uses Griffe to
resolve type names to their source modules and emit hyperlinks. The package is
loaded once per run (via build_symbol_cache()), then the resulting
symbol → module dict is reused across all files — making cross-reference
generation fast (~10 s total, down from ~3 min with the old per-symbol load).
build.py passes --source-dir mellea automatically if the directory exists.
To disable cross-references (e.g. if Griffe is not installed), omit --source-dir.
Running decorate_api_mdx.py on already-decorated files corrupts them.
Each decorator (inject_preamble, decorate_mdx_body, add_cross_references)
appends or wraps without checking whether its output already exists.
Always run the full pipeline from scratch:
uv run poe apidocs # generate-ast.py replaces api/ entirely, then decorates fresh filesNever run decorate_api_mdx.py standalone on files that have already been
decorated.
# Run all unit tests
uv run poe apidocs-test
# Run a specific test file
uv run pytest tooling/docs-autogen/test_escape_mdx.py -v
uv run pytest tooling/docs-autogen/test_cli_reference.py -v
# Find MDX files not in docs.json navigation
uv run poe apidocs-orphansTo add a new decoration step:
- Add the function to
decorate_api_mdx.py - Call it from
process_mdx_file()in the correct order - Write unit tests in a
test_*.pyfile - Update this README
Despite validate.py reporting no MDX syntax errors, the Mintlify dev server
shows parse errors in a handful of files (tracebacks, multiline JSON examples).
The enhanced blockquote/escaping logic in escape_mdx_syntax() addresses the
most common cases but has not been fully verified end-to-end.
| Symptom | Fix |
|---|---|
No module named 'mdxify' |
uv add --dev mdxify griffe |
Could not parse expression with acorn |
Unescaped {} — run uv run poe apidocs to regenerate |
VIRTUAL_ENV … does not match warning |
Harmless — uv run uses the project venv regardless |
| Port 3000 in use | lsof -ti:3000 | xargs kill -9 |
mint dev is not supported on node versions 25+ |
Switch to Node.js LTS v22 via your version manager (nvm, volta, fnm) |
| API Reference tab shows 404 locally | Generated artefacts are gitignored — run uv run poe apidocs first |
| Duplicate preamble / double dividers in MDX | Files were decorated twice — run uv run poe apidocs (starts from fresh generation) |
Griffe loading wrong package when using --source-dir |
Expected — Griffe uses try_relative_path=False to avoid loading same-named packages from CWD |