Centralize Skill IR artifact discovery
This commit is contained in:
@@ -10,6 +10,8 @@ try:
|
||||
except ImportError: # pragma: no cover
|
||||
yaml = None
|
||||
|
||||
from skill_ir_paths import find_skill_ir as find_skill_ir_document
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
COMPILER_SCHEMA_VERSION = "1.0"
|
||||
@@ -148,20 +150,7 @@ def read_frontmatter(path: Path) -> dict[str, Any]:
|
||||
|
||||
|
||||
def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict[str, Any], str]:
|
||||
candidates = [
|
||||
skill_dir / "reports" / "skill-ir.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{name}.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
|
||||
]
|
||||
seen: set[Path] = set()
|
||||
for path in candidates:
|
||||
if path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
payload = load_json(path)
|
||||
if payload:
|
||||
return payload, display_path(path, skill_dir)
|
||||
return {}, "frontmatter-fallback"
|
||||
return find_skill_ir_document(skill_dir, name, fallback_source="frontmatter-fallback")
|
||||
|
||||
|
||||
def count_list(payload: dict[str, Any], key: str) -> int:
|
||||
|
||||
@@ -7,6 +7,7 @@ from pathlib import Path, PurePosixPath
|
||||
import yaml
|
||||
|
||||
from compile_skill import compile_target_contract
|
||||
from skill_ir_paths import find_skill_ir as find_skill_ir_document
|
||||
|
||||
|
||||
def display_path(path: Path, root: Path) -> str:
|
||||
@@ -48,20 +49,7 @@ def read_interface(skill_dir: Path) -> dict:
|
||||
|
||||
|
||||
def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict, str]:
|
||||
candidates = [
|
||||
skill_dir / "reports" / "skill-ir.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{name}.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
|
||||
]
|
||||
seen = set()
|
||||
for path in candidates:
|
||||
if path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
payload = read_json(path)
|
||||
if payload:
|
||||
return payload, display_path(path, skill_dir)
|
||||
return {}, "frontmatter-fallback"
|
||||
return find_skill_ir_document(skill_dir, name, fallback_source="frontmatter-fallback")
|
||||
|
||||
|
||||
def package_name_from_manifest(manifest: dict, skill_dir: Path) -> str:
|
||||
|
||||
@@ -159,7 +159,7 @@ def report_list(skill_dir: Path) -> list[str]:
|
||||
return [rel for rel in KEY_REPORTS if (skill_dir / rel).exists()]
|
||||
|
||||
|
||||
def file_list(skill_dir: Path, folder: str, suffixes: set[str] | None = None, limit: int = 80) -> list[str]:
|
||||
def file_list(skill_dir: Path, folder: str, suffixes: set[str] | None = None, limit: int | None = None) -> list[str]:
|
||||
target = skill_dir / folder
|
||||
if not target.exists():
|
||||
return []
|
||||
@@ -170,7 +170,7 @@ def file_list(skill_dir: Path, folder: str, suffixes: set[str] | None = None, li
|
||||
if suffixes is not None and path.suffix not in suffixes:
|
||||
continue
|
||||
paths.append(str(path.relative_to(skill_dir)))
|
||||
if len(paths) >= limit:
|
||||
if limit is not None and len(paths) >= limit:
|
||||
break
|
||||
return paths
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ try:
|
||||
except ImportError: # pragma: no cover
|
||||
yaml = None
|
||||
|
||||
from skill_ir_paths import find_skill_ir as find_skill_ir_document
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
DEFAULT_REGISTRY_DIR = ROOT / "registry"
|
||||
@@ -73,16 +75,7 @@ def read_frontmatter(path: Path) -> dict[str, Any]:
|
||||
|
||||
|
||||
def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict[str, Any], str]:
|
||||
candidates = [
|
||||
skill_dir / "reports" / "skill-ir.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{name}.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
|
||||
]
|
||||
for path in candidates:
|
||||
payload = load_json(path)
|
||||
if payload:
|
||||
return payload, display_path(path, skill_dir)
|
||||
return {}, "missing"
|
||||
return find_skill_ir_document(skill_dir, name, fallback_source="missing")
|
||||
|
||||
|
||||
def license_id(skill_dir: Path) -> str:
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Any
|
||||
|
||||
from evidence_consistency_release import build_release_evidence_flow_check
|
||||
from evidence_consistency_world_class import build_world_class_workflow_check
|
||||
from skill_ir_paths import find_skill_ir_path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
@@ -122,28 +123,6 @@ def rel_path(path: Path, root: Path) -> str:
|
||||
return str(path.resolve())
|
||||
|
||||
|
||||
def canonical_skill_ir_path(skill_dir: Path, skill_name: str) -> str:
|
||||
candidates = [
|
||||
skill_dir / "reports" / "skill-ir.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_name}.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
|
||||
]
|
||||
examples_dir = skill_dir / "skill-ir" / "examples"
|
||||
if examples_dir.exists():
|
||||
for path in sorted(examples_dir.glob("*.json")):
|
||||
if path not in candidates:
|
||||
candidates.append(path)
|
||||
seen: set[Path] = set()
|
||||
for path in candidates:
|
||||
if path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
payload, failure = load_json(path)
|
||||
if not failure and payload.get("schema_version"):
|
||||
return rel_path(path, skill_dir)
|
||||
return ""
|
||||
|
||||
|
||||
def nested(payload: dict[str, Any], path: list[str], default: Any = None) -> Any:
|
||||
current: Any = payload
|
||||
for key in path:
|
||||
@@ -368,7 +347,7 @@ def build_report(skill_dir: Path, generated_at: str) -> dict[str, Any]:
|
||||
detail="The benchmark release lock must reflect the generation-time git dirty flag.",
|
||||
)
|
||||
skill_name = str(overview.get("name") or nested(review_studio, ["data", "frontmatter", "name"]) or skill_dir.name)
|
||||
expected_skill_ir_path = canonical_skill_ir_path(skill_dir, skill_name)
|
||||
expected_skill_ir_path = find_skill_ir_path(skill_dir, skill_name, require_schema=True)
|
||||
expected_skill_ir = {
|
||||
"source_path": expected_skill_ir_path,
|
||||
"exists": bool(expected_skill_ir_path and (skill_dir / expected_skill_ir_path).exists()),
|
||||
|
||||
@@ -5,6 +5,8 @@ from pathlib import Path
|
||||
|
||||
import yaml
|
||||
|
||||
from skill_ir_paths import find_skill_ir
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
@@ -18,19 +20,7 @@ def load_yaml(path: Path) -> dict:
|
||||
|
||||
|
||||
def find_ir(root: Path) -> tuple[dict, str]:
|
||||
candidates = [
|
||||
root / "reports" / "skill-ir.json",
|
||||
root / "skill-ir" / "examples" / f"{root.name}.json",
|
||||
]
|
||||
examples_dir = root / "skill-ir" / "examples"
|
||||
if examples_dir.exists():
|
||||
for path in sorted(examples_dir.glob("*.json")):
|
||||
if path not in candidates:
|
||||
candidates.append(path)
|
||||
for path in candidates:
|
||||
if path.exists():
|
||||
return load_json(path), str(path.relative_to(root))
|
||||
return {}, "missing"
|
||||
return find_skill_ir(root, root.name, fallback_source="missing")
|
||||
|
||||
|
||||
def band_for(score: int) -> str:
|
||||
|
||||
@@ -5,6 +5,8 @@ import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from skill_ir_paths import find_skill_ir_path as find_skill_ir_path_for_name
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError: # pragma: no cover
|
||||
@@ -56,34 +58,10 @@ def parse_frontmatter(path: Path) -> dict[str, Any]:
|
||||
return data
|
||||
|
||||
|
||||
def display_path(path: Path, root: Path) -> str:
|
||||
try:
|
||||
return str(path.resolve().relative_to(root.resolve()))
|
||||
except ValueError:
|
||||
return str(path)
|
||||
|
||||
|
||||
def find_skill_ir_path(skill_dir: Path) -> str:
|
||||
frontmatter = parse_frontmatter(skill_dir / "SKILL.md")
|
||||
name = str(frontmatter.get("name") or skill_dir.name)
|
||||
candidates = [
|
||||
skill_dir / "reports" / "skill-ir.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{name}.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
|
||||
]
|
||||
examples_dir = skill_dir / "skill-ir" / "examples"
|
||||
if examples_dir.exists():
|
||||
for path in sorted(examples_dir.glob("*.json")):
|
||||
if path not in candidates:
|
||||
candidates.append(path)
|
||||
seen: set[Path] = set()
|
||||
for path in candidates:
|
||||
if path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
if path.exists():
|
||||
return display_path(path, skill_dir)
|
||||
return ""
|
||||
return find_skill_ir_path_for_name(skill_dir, name)
|
||||
|
||||
|
||||
def evidence_paths(skill_dir: Path) -> dict[str, str]:
|
||||
|
||||
@@ -10,6 +10,8 @@ try:
|
||||
except ImportError: # pragma: no cover
|
||||
yaml = None
|
||||
|
||||
from skill_ir_paths import find_skill_ir as find_skill_ir_document
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
DEFAULT_TARGETS = ["openai", "claude", "agent-skills", "vscode", "generic"]
|
||||
@@ -64,10 +66,7 @@ def parse_frontmatter(path: Path) -> tuple[dict[str, Any], str]:
|
||||
|
||||
|
||||
def find_skill_ir(skill_dir: Path, name: str) -> dict[str, Any]:
|
||||
direct = load_json(skill_dir / "reports" / "skill-ir.json")
|
||||
if direct:
|
||||
return direct
|
||||
return load_json(skill_dir / "skill-ir" / "examples" / f"{name}.json")
|
||||
return find_skill_ir_document(skill_dir, name)[0]
|
||||
|
||||
|
||||
def add_check(checks: list[str], failures: list[str], condition: bool, passed: str, failed: str) -> None:
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Shared Skill IR artifact discovery helpers."""
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
|
||||
SCRIPT_INTERFACE = "internal-module"
|
||||
SCRIPT_INTERFACE_REASON = "Imported by compiler, registry, conformance, and report scripts to locate canonical Skill IR artifacts."
|
||||
|
||||
|
||||
def display_path(path: Path, root: Path) -> str:
|
||||
try:
|
||||
return str(path.resolve().relative_to(root.resolve()))
|
||||
except ValueError:
|
||||
return str(path.resolve())
|
||||
|
||||
|
||||
def candidate_paths(skill_dir: Path, name: str) -> list[Path]:
|
||||
candidates = [
|
||||
skill_dir / "reports" / "skill-ir.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{name}.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
|
||||
]
|
||||
examples_dir = skill_dir / "skill-ir" / "examples"
|
||||
if examples_dir.exists():
|
||||
for path in sorted(examples_dir.glob("*.json")):
|
||||
if path not in candidates:
|
||||
candidates.append(path)
|
||||
seen: set[Path] = set()
|
||||
unique: list[Path] = []
|
||||
for path in candidates:
|
||||
if path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
unique.append(path)
|
||||
return unique
|
||||
|
||||
|
||||
def load_json(path: Path) -> dict[str, Any]:
|
||||
if not path.exists():
|
||||
return {}
|
||||
try:
|
||||
payload = json.loads(path.read_text(encoding="utf-8"))
|
||||
except json.JSONDecodeError:
|
||||
return {}
|
||||
return payload if isinstance(payload, dict) else {}
|
||||
|
||||
|
||||
def find_skill_ir_path(skill_dir: Path, name: str, *, require_schema: bool = False, fallback_source: str = "") -> str:
|
||||
for path in candidate_paths(skill_dir, name):
|
||||
payload = load_json(path)
|
||||
if not payload:
|
||||
continue
|
||||
if require_schema and not payload.get("schema_version"):
|
||||
continue
|
||||
return display_path(path, skill_dir)
|
||||
return fallback_source
|
||||
|
||||
|
||||
def find_skill_ir(
|
||||
skill_dir: Path,
|
||||
name: str,
|
||||
*,
|
||||
require_schema: bool = False,
|
||||
fallback_source: str = "",
|
||||
) -> tuple[dict[str, Any], str]:
|
||||
for path in candidate_paths(skill_dir, name):
|
||||
payload = load_json(path)
|
||||
if not payload:
|
||||
continue
|
||||
if require_schema and not payload.get("schema_version"):
|
||||
continue
|
||||
return payload, display_path(path, skill_dir)
|
||||
return {}, fallback_source
|
||||
@@ -4,6 +4,7 @@ import re
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
|
||||
from skill_ir_paths import find_skill_ir
|
||||
from skill_report_metrics import calculate_scorecard
|
||||
from skill_report_world_class import world_class_readiness, world_class_roadmap_item
|
||||
|
||||
@@ -84,35 +85,6 @@ def load_json(path: Path) -> dict:
|
||||
return payload if isinstance(payload, dict) else {}
|
||||
|
||||
|
||||
def display_path(path: Path, root: Path) -> str:
|
||||
try:
|
||||
return str(path.resolve().relative_to(root.resolve()))
|
||||
except ValueError:
|
||||
return str(path)
|
||||
|
||||
|
||||
def find_skill_ir(skill_dir: Path, name: str) -> tuple[dict, str]:
|
||||
candidates = [
|
||||
skill_dir / "reports" / "skill-ir.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{name}.json",
|
||||
skill_dir / "skill-ir" / "examples" / f"{skill_dir.name}.json",
|
||||
]
|
||||
examples_dir = skill_dir / "skill-ir" / "examples"
|
||||
if examples_dir.exists():
|
||||
for path in sorted(examples_dir.glob("*.json")):
|
||||
if path not in candidates:
|
||||
candidates.append(path)
|
||||
seen: set[Path] = set()
|
||||
for path in candidates:
|
||||
if path in seen:
|
||||
continue
|
||||
seen.add(path)
|
||||
payload = load_json(path)
|
||||
if payload:
|
||||
return payload, display_path(path, skill_dir)
|
||||
return {}, ""
|
||||
|
||||
|
||||
def extract_title(body: str, fallback: str) -> str:
|
||||
for line in body.splitlines():
|
||||
if line.startswith("# "):
|
||||
|
||||
Reference in New Issue
Block a user