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
API
Functions
| Function | Signature | Summary |
|---|---|---|
main |
main() -> None |
CLI entry point for parse_spec_requirements. |
Source
#!/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()