validate-package.py 2.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. #!/usr/bin/env python3
  2. """Validate Humanizer's portable package surfaces without external dependencies."""
  3. from __future__ import annotations
  4. import json
  5. import re
  6. from pathlib import Path
  7. ROOT = Path(__file__).resolve().parent.parent
  8. SKILL = (ROOT / "SKILL.md").read_text(encoding="utf-8")
  9. README = (ROOT / "README.md").read_text(encoding="utf-8")
  10. PLUGIN = json.loads((ROOT / ".claude-plugin" / "plugin.json").read_text(encoding="utf-8"))
  11. PLUGIN_SKILL = ROOT / "skills" / "humanizer" / "SKILL.md"
  12. def require(match: re.Match[str] | None, message: str) -> re.Match[str]:
  13. if match is None:
  14. raise SystemExit(message)
  15. return match
  16. frontmatter = require(
  17. re.match(r"\A---\n(.*?)\n---\n", SKILL, re.DOTALL),
  18. "SKILL.md must start with YAML frontmatter",
  19. ).group(1)
  20. for nonportable_key in ("compatibility:", "allowed-tools:"):
  21. if re.search(rf"(?m)^{re.escape(nonportable_key)}", frontmatter):
  22. raise SystemExit(f"Remove nonportable frontmatter key: {nonportable_key[:-1]}")
  23. skill_version = require(
  24. re.search(r'(?m)^\s+version:\s*["\']([^"\']+)["\']\s*$', frontmatter),
  25. "SKILL.md metadata.version is missing",
  26. ).group(1)
  27. readme_version = require(
  28. re.search(r"(?m)^- \*\*([0-9]+\.[0-9]+\.[0-9]+)\*\*", README),
  29. "README version history is missing",
  30. ).group(1)
  31. versions = {skill_version, readme_version, str(PLUGIN.get("version", ""))}
  32. if len(versions) != 1:
  33. raise SystemExit(f"Version mismatch: {sorted(versions)}")
  34. if not PLUGIN_SKILL.is_symlink():
  35. raise SystemExit("skills/humanizer/SKILL.md must link to the canonical root SKILL.md")
  36. if PLUGIN_SKILL.resolve() != (ROOT / "SKILL.md").resolve():
  37. raise SystemExit("skills/humanizer/SKILL.md must resolve to the root SKILL.md")
  38. pattern_numbers = [
  39. int(number)
  40. for number in re.findall(r"(?m)^### ([0-9]+)\. ", SKILL)
  41. ]
  42. if pattern_numbers != list(range(1, 36)):
  43. raise SystemExit(f"Expected patterns 1-35, found {pattern_numbers}")
  44. readme_numbers = {
  45. int(number) for number in re.findall(r"(?m)^\| ([0-9]+) \|", README)
  46. }
  47. if readme_numbers != set(range(1, 36)):
  48. raise SystemExit("README pattern table must contain patterns 1-35")
  49. if len(SKILL.splitlines()) > 500:
  50. raise SystemExit("SKILL.md exceeds the 500-line portability budget")
  51. print(f"Humanizer package v{skill_version} is valid")