Files
stack/docs/scripts/extract_cli.py
kert ec44e937f5 add Docusaurus docs generation: API reference, CLI, REST API, citations, DAG viz
Wire cli/docs.py build/serve/generate commands to run extraction scripts
and Docusaurus. Add extract_cli.py (27 CLI docs from typer help),
extract_openapi.py (8 REST API docs from FastAPI schema),
extract_citations.py (14 pipeline pages with bib references),
extract_dag.py (pipeline + table lineage Mermaid diagrams).
Closes #26, #27, #28, #29, #30.
2026-03-12 17:38:19 -04:00

102 lines
2.7 KiB
Python

"""Extract CLI usage docs from typer commands.
Walks the CLI app tree, invokes ``--help`` for each command, and
writes Markdown files into ``docs/docs/cli/``.
Usage::
uv run python docs/scripts/extract_cli.py
"""
from __future__ import annotations
import sys
from pathlib import Path
from typer.testing import CliRunner
# Ensure src/ is on the path
sys.path.insert(0, str(Path("src").resolve()))
from cli import app # noqa: E402
runner = CliRunner()
OUT_DIR = Path("docs/docs/cli")
def _get_commands(group_name: str = "") -> list[tuple[str, list[str]]]:
"""Walk the typer app tree and return (name, cmd_path) pairs."""
commands: list[tuple[str, list[str]]] = []
# Top-level help
commands.append(("stack", []))
# Registered sub-commands / groups
for info in app.registered_commands:
name = info.name or info.callback.__name__
commands.append((name, [name]))
for group in app.registered_groups:
grp_name = group.name or ""
if not grp_name:
continue
commands.append((grp_name, [grp_name]))
# Walk sub-commands within the group
grp_app = group.typer_instance
if grp_app:
for sub_info in grp_app.registered_commands:
sub_name = sub_info.name or sub_info.callback.__name__
commands.append(
(f"{grp_name}-{sub_name}", [grp_name, sub_name])
)
for sub_group in grp_app.registered_groups:
sg_name = sub_group.name or ""
if sg_name:
commands.append(
(f"{grp_name}-{sg_name}", [grp_name, sg_name])
)
return commands
def main() -> None:
if OUT_DIR.exists():
import shutil
shutil.rmtree(OUT_DIR)
OUT_DIR.mkdir(parents=True, exist_ok=True)
# Category metadata for sidebar
cat_json = OUT_DIR / "_category_.json"
cat_json.write_text('{"label": "CLI Reference", "position": 10}\n')
commands = _get_commands()
count = 0
for slug, cmd_path in commands:
result = runner.invoke(app, [*cmd_path, "--help"])
if result.exit_code != 0:
continue
help_text = result.output.strip()
title = f"stack {' '.join(cmd_path)}" if cmd_path else "stack"
lines = [
f"---\ntitle: {title}\nsidebar_position: {count + 1}\n---\n",
f"# `{title}`\n",
f"```\n{help_text}\n```\n",
]
out_path = OUT_DIR / f"{slug}.md"
out_path.write_text("\n".join(lines))
count += 1
print(f" {title} -> {out_path}")
print(f"\nDone: {count} CLI docs written to {OUT_DIR}")
if __name__ == "__main__":
main()