Ctrl K
ComunidadGuías…

scripts/generate-package-readmes.py

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
"""Generate packages/<pkg>/README.md from templates/package-purposes.yml.

Run from repo root:
    python scripts/generate-package-readmes.py

Idempotent — overwrites existing READMEs.
"""

from __future__ import annotations

import posixpath
import sys
from pathlib import Path

try:
    import yaml  # type: ignore[import-untyped]
except ImportError:
    sys.stderr.write(
        "PyYAML missing. Install with `pip install pyyaml` (or `uv add pyyaml`).\n"
    )
    sys.exit(1)


REPO_ROOT = Path(__file__).resolve().parent.parent
PURPOSES_FILE = REPO_ROOT / "templates" / "package-purposes.yml"
PACKAGES_DIR = REPO_ROOT / "packages"


def link_from_package(pkg_path: str, target: str) -> str:
    """Return a repository-relative link target from a package README."""
    return posixpath.relpath(target, posixpath.join("packages", pkg_path))


def test_command(pkg_path: str, language: str) -> str:
    """Return the package's test command block.

    Every command is emitted on its own line rather than joined with ``&&``:
    Windows PowerShell 5.1 has no ``&&`` operator, so a chained recipe is not
    runnable on a supported platform.

    Args:
        pkg_path: Package path relative to ``packages/``, e.g. ``core/ticks``.
        language: Language label from ``templates/package-purposes.yml``.

    Returns:
        A newline-separated command block, safe to paste into any shell.
    """
    if pkg_path == "apps/site":
        return (
            "pnpm --filter @flinttrade/site typecheck\n"
            "pnpm --filter @flinttrade/site test\n"
            "pnpm --filter @flinttrade/site build"
        )
    if pkg_path == "apps/terminal":
        return (
            "pnpm --filter @flinttrade/terminal test:base\n"
            "pnpm --filter @flinttrade/terminal build"
        )
    if pkg_path == "core/design-system":
        return "cd packages/core/design-system\nnpx tsc --noEmit"
    if language in {"Python", "Python + Numba"}:
        return f"python -m pytest packages/{pkg_path}/tests/ -v --import-mode=importlib"
    if language == "Rust + PyO3":
        return f"cd packages/{pkg_path}\ncargo test"
    return f"# See packages/{pkg_path}/ for test instructions"


def install_command(pkg_path: str, language: str) -> str:
    """Return the package's install command block.

    One command per line, for the same cross-platform reason as
    :func:`test_command`.

    Args:
        pkg_path: Package path relative to ``packages/``, e.g. ``core/ticks``.
        language: Language label from ``templates/package-purposes.yml``.

    Returns:
        A newline-separated command block, safe to paste into any shell.
    """
    if language.startswith("TypeScript"):
        return "pnpm install"
    if language == "Rust + PyO3":
        return f"uv pip install -e packages/{pkg_path}\ncd packages/{pkg_path}\ncargo test"
    return f"uv pip install -e packages/{pkg_path}"


def render_readme(pkg_name: str, info: dict[str, object]) -> str:
    purpose = info["purpose"]
    language = info["language"]
    entry_points = info["entry_points"]

    assert isinstance(purpose, str)
    assert isinstance(language, str)
    assert isinstance(entry_points, list)

    # Human-friendly package title.
    package_leaf = pkg_name.split("/")[-1]
    title = package_leaf.replace("-", " ").title().replace("Ai", "AI")
    if package_leaf == "ticks":
        title = "Tick Engine"
    if pkg_name == "services/backtest":
        title = "Backtest Engine"
    if pkg_name == "apps/site":
        title = "@flinttrade/site"
    if pkg_name == "apps/terminal":
        title = "Terminal"
    if pkg_name == "core/design-system":
        title = "@flinttrade/design-system"

    test_cmd_rendered = test_command(pkg_name, language)
    install_cmd_rendered = install_command(pkg_name, language)
    developer_guide = link_from_package(pkg_name, "docs/DEVELOPER_GUIDE.md")
    architecture = link_from_package(pkg_name, "docs/ARCHITECTURE.md")
    user_guide = link_from_package(pkg_name, "docs/USER_GUIDE.md")
    contributing = link_from_package(pkg_name, "contributing.md")
    license_path = link_from_package(pkg_name, "LICENSE")

    entry_lines = "\n".join(f"- `{ep}`" for ep in entry_points)

    return f"""# {title}

> {purpose}

**Part of [FlintTrade](https://github.com/navaneeshnagarajan/FlintTrade)** — the open-source self-hosted trading software monorepo built with Python, React, TypeScript, and Rust.

**Language:** {language}

## Public surface

{entry_lines}

(See the source for the full surface.)

## Install

This package is part of the FlintTrade monorepo. Install via the workspace from the repo root:

```bash
{install_cmd_rendered}
```

If you only want to use the package in isolation, the package's `pyproject.toml`,
`Cargo.toml`, or `package.json` lists its dependencies. The supported path is the
root workspace.

## Tests

```bash
{test_cmd_rendered}
```

Run one command per line. They work unchanged in bash, zsh and Windows
PowerShell — do not join them with `&&`, which Windows PowerShell 5.1 does not
support.

For the full test matrix, see the contributor guide at [docs/DEVELOPER_GUIDE.md]({developer_guide}).

## How this fits in

This package's role in the wider FlintTrade architecture is documented in
[docs/ARCHITECTURE.md]({architecture}). For end-user features it powers, see
[docs/USER_GUIDE.md]({user_guide}).

## Contributing

Contributions welcome. Please read [`contributing.md`]({contributing}) at the repo root before opening a pull request.

## License

AGPL-3.0 — same as the parent repository. See [`LICENSE`]({license_path}) for the full text.
"""


def main() -> int:
    if not PURPOSES_FILE.exists():
        sys.stderr.write(f"Purposes file not found: {PURPOSES_FILE}\n")
        return 1

    with PURPOSES_FILE.open("r", encoding="utf-8") as f:
        data = yaml.safe_load(f)

    written = 0
    skipped = 0
    for pkg_name, info in data.items():
        pkg_dir = PACKAGES_DIR / Path(pkg_name)
        if not pkg_dir.exists():
            sys.stderr.write(f"Package directory missing: {pkg_dir}\n")
            skipped += 1
            continue
        readme_path = pkg_dir / "README.md"
        readme_path.write_text(render_readme(pkg_name, info), encoding="utf-8")
        written += 1

    print(f"Generated {written} package READMEs.")
    if skipped:
        print(f"Skipped {skipped} (missing package directories).")
    return 0


if __name__ == "__main__":
    sys.exit(main())