"""Pipeline manifest loader. Loads and validates pipeline YAML manifests from pipeline_defs/. """ from __future__ import annotations import json from pathlib import Path from typing import Any, Optional import yaml import jsonschema PIPELINE_DEFS_DIR = Path(__file__).resolve().parent.parent / "pipeline_defs" SCHEMA_PATH = ( Path(__file__).resolve().parent.parent / "schemas" / "pipelines" / "pipeline_manifest.schema.json" ) def _load_manifest_schema() -> dict: with open(SCHEMA_PATH) as f: return json.load(f) def load_pipeline(name: str, defs_dir: Optional[Path] = None) -> dict[str, Any]: """Load and validate a pipeline manifest by name. Args: name: Pipeline name (without .yaml extension). defs_dir: Override directory for pipeline definitions. Returns: Validated pipeline manifest dict. """ defs_dir = defs_dir or PIPELINE_DEFS_DIR path = defs_dir / f"{name}.yaml" if not path.exists(): raise FileNotFoundError(f"Pipeline manifest not found: {path}") with open(path) as f: manifest = yaml.safe_load(f) schema = _load_manifest_schema() jsonschema.validate(instance=manifest, schema=schema) return manifest def list_pipelines(defs_dir: Optional[Path] = None) -> list[str]: """List all available pipeline manifest names.""" defs_dir = defs_dir or PIPELINE_DEFS_DIR return [p.stem for p in defs_dir.glob("*.yaml")] def get_stage_order(manifest: dict) -> list[str]: """Extract the ordered list of stage names from a manifest.""" return [stage["name"] for stage in manifest["stages"]] def get_required_tools(manifest: dict) -> set[str]: """Collect all preferred + fallback + available tools across all stages.""" tools: set[str] = set() for stage in manifest["stages"]: tools.update(stage.get("preferred_tools", [])) tools.update(stage.get("fallback_tools", [])) tools.update(stage.get("tools_available", [])) return tools def get_stage_skill(manifest: dict, stage_name: str) -> Optional[str]: """Get the skill path for an instruction-driven stage.""" for stage in manifest["stages"]: if stage["name"] == stage_name: return stage.get("skill") return None def get_stage_review_focus(manifest: dict, stage_name: str) -> list[str]: """Get the review focus items for a stage.""" for stage in manifest["stages"]: if stage["name"] == stage_name: return stage.get("review_focus", []) return []