scripts.parse_spec_requirements

Reference › scripts › parse_spec_requirements

scripts/parse_spec_requirements.py is a thin command-line shim over the spec-requirements parser in orchestrata_ultimate.openspec.requirements_parser. A human or a spec-authoring step runs it.

It takes two positional arguments, a spec id and the path to the spec's checklist; without both it prints its usage to standard error and exits 1. With only those it dumps the facts parsed from the checklist. With --next-story STORY (for example US-004, US4 or 4) it instead prints the next free requirement id for that story, computed by next_requirement_id from the checklist and the spec's spec.md. --spec-md overrides the spec.md path, which otherwise is derived from the checklist's spec directory. A missing file or invalid story is reported as an error. It reads the spec files and writes nothing.

At a glance

Source scripts/parse_spec_requirements.py
Lines of code 86
Classes 0
Functions 1
Imports (internal) orchestrata_ultimate.openspec.requirements_parser
Imported by none
Tests that import it none

Class diagram

No classes; see Functions.

Dependency diagram

Dependency diagram scripts.parse_spec_requirements

API

Functions

Function Signature Summary
main main() -> None CLI entry point for parse_spec_requirements.

Source

Permalink: https://gitlab.com/MSamy/orchestrata-ultimate-architecture/-/blob/a4cbc2bdf/scripts/parse_spec_requirements.py

#!/usr/bin/env python3
"""Thin CLI shim for the spec-requirements parser.

Usage:
    python3 scripts/parse_spec_requirements.py 014 \
        specs/014-websocket-gateway/checklists/requirements.md

    # Emit the next correct requirement id for a story instead of dumping
    # facts -- the automatic seam, so nobody hand-types (or hand-mistypes)
    # a requirement id again:
    python3 scripts/parse_spec_requirements.py 003 \
        specs/003-intent/checklists/requirements.md --next-story US-004
"""

from __future__ import annotations

import argparse
import sys
from pathlib import Path

# Repo root on sys.path so `scripts` (imported transitively below, by
# requirements_parser's story-existence check) resolves regardless of how
# this file is invoked (direct `python scripts/parse_spec_requirements.py`,
# `-m`, or as a test import) -- same pattern as scripts/derive_rtm.py /
# scripts/check_wiring.py. Direct execution only puts THIS file's own
# directory (scripts/) on sys.path[0], not its parent, so `scripts.*`
# wouldn't otherwise resolve.
_REPO_ROOT = Path(__file__).resolve().parent.parent
if str(_REPO_ROOT) not in sys.path:
    sys.path.insert(0, str(_REPO_ROOT))

from orchestrata_ultimate.openspec.requirements_parser import (  # noqa: E402
    next_requirement_id,
    parse_requirements,
)


def main() -> None:
    """CLI entry point for parse_spec_requirements."""
    parser = argparse.ArgumentParser(
        prog="parse_spec_requirements.py",
        usage="Usage: parse_spec_requirements.py <spec_id> <checklist_path> "
        "[--next-story STORY [--spec-md PATH]]",
    )
    parser.add_argument("spec_id", nargs="?")
    parser.add_argument("checklist_path", nargs="?")
    parser.add_argument(
        "--next-story",
        metavar="STORY",
        default=None,
        help="Emit the next requirement id for STORY (e.g. US-004, US4, 4) "
        "instead of dumping facts.",
    )
    parser.add_argument(
        "--spec-md",
        default=None,
        help="Path to the spec's spec.md (default: derived as "
        "<checklist_path's spec dir>/spec.md).",
    )
    args = parser.parse_args()

    if args.spec_id is None or args.checklist_path is None:
        parser.print_usage(sys.stderr)
        sys.exit(1)

    if args.next_story is not None:
        spec_md_path = args.spec_md or str(
            Path(args.checklist_path).resolve().parent.parent / "spec.md"
        )
        try:
            print(
                next_requirement_id(
                    args.spec_id, args.next_story, args.checklist_path, spec_md_path
                )
            )
        except (FileNotFoundError, ValueError) as exc:
            print(f"error: {exc}", file=sys.stderr)
            sys.exit(1)
        return

    for fact in parse_requirements(args.spec_id, args.checklist_path):
        print(f"{fact}.")


if __name__ == "__main__":
    main()