Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
A comprehensive collection of Agent Skills for context engineering, multi-agent architectures, and production agent systems.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
researcher/scripts/compare_skill_revisions.py
1#!/usr/bin/env python32"""Deterministic pre-check for pairwise skill revision review."""34from __future__ import annotations56import argparse7import json8import re9from pathlib import Path10from typing import Any111213def section_set(text: str) -> set[str]:14return set(re.findall(r"^##\s+(.+)$", text, flags=re.MULTILINE))151617def parse_frontmatter(text: str) -> dict[str, str]:18if not text.startswith("---\n"):19return {}20end = text.find("\n---", 4)21if end == -1:22return {}23result: dict[str, str] = {}24for line in text[4:end].splitlines():25if ":" in line and not line.startswith(" "):26key, value = line.split(":", 1)27result[key.strip()] = value.strip().strip('"')28return result293031def candidate_metrics(path: Path) -> dict[str, Any]:32text = path.read_text(encoding="utf-8")33frontmatter = parse_frontmatter(text)34sections = section_set(text)35words = re.findall(r"[A-Za-z0-9_-]+", text)36gotchas = 037if "Gotchas" in sections:38gotchas_match = re.search(r"## Gotchas\n(.+?)(?:\n## |\Z)", text, flags=re.DOTALL)39if gotchas_match:40gotchas = len(re.findall(r"^\d+\.", gotchas_match.group(1), flags=re.MULTILINE))41return {42"path": str(path),43"name": frontmatter.get("name", ""),44"description_present": bool(frontmatter.get("description")),45"line_count": len(text.splitlines()),46"word_count": len(words),47"sections": sorted(sections),48"gotcha_count": gotchas,49"has_references": "References" in sections,50"has_integration": "Integration" in sections,51"has_guidelines": "Guidelines" in sections,52}535455def structural_score(metrics: dict[str, Any]) -> float:56score = 0.057if metrics["name"]:58score += 0.1559if metrics["description_present"]:60score += 0.1561if metrics["line_count"] <= 500:62score += 0.263if metrics["gotcha_count"] >= 3:64score += 0.1565if metrics["has_guidelines"]:66score += 0.167if metrics["has_integration"]:68score += 0.169if metrics["has_references"]:70score += 0.0571if metrics["line_count"] <= 300:72score += 0.173return round(score, 3)747576def main() -> int:77parser = argparse.ArgumentParser(description="Compare two skill revisions before rubric review")78parser.add_argument("candidate_a", type=Path)79parser.add_argument("candidate_b", type=Path)80parser.add_argument("--json", action="store_true")81args = parser.parse_args()8283a = candidate_metrics(args.candidate_a)84b = candidate_metrics(args.candidate_b)85a["structural_score"] = structural_score(a)86b["structural_score"] = structural_score(b)87shared_sections = sorted(set(a["sections"]) & set(b["sections"]))88section_delta = {89"only_a": sorted(set(a["sections"]) - set(b["sections"])),90"only_b": sorted(set(b["sections"]) - set(a["sections"])),91"shared": shared_sections,92}9394if a["structural_score"] > b["structural_score"]:95recommendation = "A"96elif b["structural_score"] > a["structural_score"]:97recommendation = "B"98elif a["line_count"] < b["line_count"]:99recommendation = "A"100elif b["line_count"] < a["line_count"]:101recommendation = "B"102else:103recommendation = "human_review"104105result = {106"candidate_a": a,107"candidate_b": b,108"section_delta": section_delta,109"deterministic_recommendation": recommendation,110"note": "Use researcher/rubrics/pairwise-skill-revision.md for final semantic judgment.",111}112if args.json:113print(json.dumps(result, indent=2))114else:115print(f"Recommendation: {recommendation}")116print(f"A score={a['structural_score']} lines={a['line_count']} gotchas={a['gotcha_count']}")117print(f"B score={b['structural_score']} lines={b['line_count']} gotchas={b['gotcha_count']}")118return 0119120121if __name__ == "__main__":122raise SystemExit(main())123