Centralize Skill IR artifact discovery

This commit is contained in:
yaojingang
2026-06-16 03:12:10 +08:00
parent e31eee53f3
commit 291e649fac
39 changed files with 543 additions and 266 deletions
+3 -14
View File
@@ -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:
+2 -14
View File
@@ -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:
+2 -2
View File
@@ -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
+3 -10
View File
@@ -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:
+2 -23
View File
@@ -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()),
+3 -13
View File
@@ -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:
+3 -25
View File
@@ -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]:
+3 -4
View File
@@ -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:
+76
View File
@@ -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
+1 -29
View File
@@ -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("# "):