Files
antigravity-skills-reference/tools/scripts/update_readme.py
2026-03-13 09:24:36 +01:00

309 lines
10 KiB
Python

#!/usr/bin/env python3
import argparse
import io
import json
import os
import re
import sys
import urllib.error
import urllib.request
from datetime import datetime, timezone
GITHUB_REPO = "sickn33/antigravity-awesome-skills"
SYNC_COMMENT_RE = re.compile(r"<!-- registry-sync: .*? -->")
SYNC_COMMENT_FIELDS_RE = re.compile(
r"<!-- registry-sync: version=(?P<version>[^;]+); skills=(?P<skills>\d+); "
r"stars=(?P<stars>\d+); updated_at=(?P<updated_at>[^ ]+) -->"
)
def configure_utf8_output() -> None:
"""Best-effort UTF-8 stdout/stderr on Windows without dropping diagnostics."""
if sys.platform != "win32":
return
for stream_name in ("stdout", "stderr"):
stream = getattr(sys, stream_name)
try:
stream.reconfigure(encoding="utf-8", errors="backslashreplace")
continue
except Exception:
pass
buffer = getattr(stream, "buffer", None)
if buffer is not None:
setattr(
sys,
stream_name,
io.TextIOWrapper(buffer, encoding="utf-8", errors="backslashreplace"),
)
def find_repo_root(start_path: str) -> str:
current = os.path.abspath(start_path)
while True:
if os.path.isfile(os.path.join(current, "package.json")) and os.path.isfile(
os.path.join(current, "README.md")
):
return current
parent = os.path.dirname(current)
if parent == current:
raise FileNotFoundError("Could not locate repository root from script path.")
current = parent
def format_skill_count(total_skills: int) -> str:
return f"{total_skills:,}+"
def format_star_badge_count(stars: int) -> str:
if stars >= 1000:
rounded = int(round(stars / 1000.0))
return f"{rounded}%2C000%2B"
return f"{stars}%2B"
def format_star_milestone(stars: int) -> str:
if stars >= 1000:
rounded = int(round(stars / 1000.0))
return f"{rounded},000+"
return f"{stars}+"
def format_star_celebration(stars: int) -> str:
if stars >= 1000:
rounded = int(round(stars / 1000.0))
return f"{rounded}k"
return str(stars)
def fetch_star_count(repo: str) -> int | None:
url = f"https://api.github.com/repos/{repo}"
request = urllib.request.Request(
url,
headers={
"Accept": "application/vnd.github+json",
"User-Agent": "antigravity-awesome-skills-readme-sync",
},
)
try:
with urllib.request.urlopen(request, timeout=10) as response:
payload = json.load(response)
except (urllib.error.URLError, TimeoutError, json.JSONDecodeError):
return None
stars = payload.get("stargazers_count")
return int(stars) if isinstance(stars, int) else None
def parse_existing_sync_metadata(current_readme: str) -> dict[str, str | int] | None:
match = SYNC_COMMENT_FIELDS_RE.search(current_readme)
if not match:
return None
return {
"version": match.group("version"),
"skills": int(match.group("skills")),
"stars": int(match.group("stars")),
"updated_at": match.group("updated_at"),
}
def load_metadata(
base_dir: str, repo: str = GITHUB_REPO, refresh_volatile: bool = False
) -> dict:
readme_path = os.path.join(base_dir, "README.md")
package_path = os.path.join(base_dir, "package.json")
index_path = os.path.join(base_dir, "skills_index.json")
with open(index_path, "r", encoding="utf-8") as file:
skills = json.load(file)
with open(package_path, "r", encoding="utf-8") as file:
package = json.load(file)
with open(readme_path, "r", encoding="utf-8") as file:
current_readme = file.read()
existing_sync_metadata = parse_existing_sync_metadata(current_readme)
current_star_match = re.search(r"%20([\d%2C\+]+)%20Stars", current_readme)
current_stars = None
if current_star_match:
compact = current_star_match.group(1).replace("%2C", "").replace("%2B", "")
compact = compact.rstrip("+")
if compact.isdigit():
current_stars = int(compact)
existing_stars = None
existing_updated_at = None
if existing_sync_metadata:
stars = existing_sync_metadata.get("stars")
updated_at = existing_sync_metadata.get("updated_at")
if isinstance(stars, int):
existing_stars = stars
if isinstance(updated_at, str):
existing_updated_at = updated_at
live_stars = fetch_star_count(repo) if refresh_volatile else None
total_stars = (
live_stars
if live_stars is not None
else existing_stars
if existing_stars is not None
else current_stars or 0
)
updated_at = (
datetime.now(timezone.utc).replace(microsecond=0).isoformat()
if refresh_volatile or existing_updated_at is None
else existing_updated_at
)
return {
"repo": repo,
"version": str(package.get("version", "0.0.0")),
"total_skills": len(skills),
"total_skills_label": format_skill_count(len(skills)),
"stars": total_stars,
"star_badge_count": format_star_badge_count(total_stars),
"star_milestone": format_star_milestone(total_stars),
"star_celebration": format_star_celebration(total_stars),
"updated_at": updated_at,
"used_live_star_count": live_stars is not None,
"refreshed_volatile": refresh_volatile,
}
def apply_metadata(content: str, metadata: dict) -> str:
total_skills = metadata["total_skills"]
total_skills_label = metadata["total_skills_label"]
version = metadata["version"]
star_badge_count = metadata["star_badge_count"]
star_milestone = metadata["star_milestone"]
star_celebration = metadata["star_celebration"]
sync_comment = (
f"<!-- registry-sync: version={version}; skills={total_skills}; "
f"stars={metadata['stars']}; updated_at={metadata['updated_at']} -->"
)
content = re.sub(
r"^# 🌌 Antigravity Awesome Skills: .*?$",
(
f"# 🌌 Antigravity Awesome Skills: {total_skills_label} "
"Agentic Skills for Claude Code, Gemini CLI, Cursor, Copilot & More"
),
content,
count=1,
flags=re.MULTILINE,
)
content = re.sub(
r"^> \*\*The Ultimate Collection of .*?\*\*$",
(
f"> **The Ultimate Collection of {total_skills_label} Universal Agentic "
"Skills for AI Coding Assistants — Claude Code, Gemini CLI, Codex CLI, "
"Antigravity IDE, GitHub Copilot, Cursor, OpenCode, AdaL**"
),
content,
count=1,
flags=re.MULTILINE,
)
content = re.sub(
r"https://img\.shields\.io/badge/⭐%20[\d%2C\+]+%20Stars-gold\?style=for-the-badge",
f"https://img.shields.io/badge/⭐%20{star_badge_count}%20Stars-gold?style=for-the-badge",
content,
count=1,
)
content = re.sub(
r"^\*\*Antigravity Awesome Skills\*\* is a curated, battle-tested library of \*\*.*?\*\* designed",
(
f"**Antigravity Awesome Skills** is a curated, battle-tested library of "
f"**{total_skills_label} high-performance agentic skills** designed"
),
content,
count=1,
flags=re.MULTILINE,
)
content = re.sub(
r"\[📚 Browse \d[\d,]*\+ Skills\]\(#browse-[^)]+\)",
f"[📚 Browse {total_skills_label} Skills](#browse-{total_skills}-skills)",
content,
count=1,
)
content = re.sub(
r"\*\*Welcome to the V[\d.]+ .*? Stars Celebration Release!\*\*",
f"**Welcome to the V{version} {star_celebration} Stars Celebration Release!**",
content,
count=1,
)
content = re.sub(
r"> \*\*🌟 .*? GitHub Stars Milestone!\*\*",
f"> **🌟 {star_milestone} GitHub Stars Milestone!**",
content,
count=1,
)
content = re.sub(
r"\*\*Antigravity Awesome Skills\*\* \(Release [\d.]+\) is a massive upgrade to your AI's capabilities, now featuring \*\*.*?\*\* skills",
(
f"**Antigravity Awesome Skills** (Release {version}) is a massive upgrade "
f"to your AI's capabilities, now featuring **{total_skills_label} skills**"
),
content,
count=1,
)
content = re.sub(
r"## Browse \d[\d,]*\+ Skills",
f"## Browse {total_skills_label} Skills",
content,
count=1,
)
content = re.sub(
r"<!-- registry-sync: .*? -->\n?",
"",
content,
count=1,
)
return f"{sync_comment}\n{content.lstrip()}"
def update_readme(dry_run: bool = False, refresh_volatile: bool = False) -> dict:
base_dir = find_repo_root(os.path.dirname(__file__))
readme_path = os.path.join(base_dir, "README.md")
metadata = load_metadata(base_dir, refresh_volatile=refresh_volatile)
print(f"📖 Reading README from: {readme_path}")
print(f"🔢 Total skills found: {metadata['total_skills']}")
print(f"🏷️ Version found: {metadata['version']}")
if metadata["used_live_star_count"]:
print(f"⭐ Live GitHub stars found: {metadata['stars']}")
else:
print(f"⭐ Using existing README star count: {metadata['stars']}")
with open(readme_path, "r", encoding="utf-8") as file:
content = file.read()
updated_content = apply_metadata(content, metadata)
if dry_run:
print("🧪 Dry run enabled; README.md not written.")
return metadata
with open(readme_path, "w", encoding="utf-8", newline="\n") as file:
file.write(updated_content)
print("✅ README.md updated successfully.")
return metadata
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Sync generated metadata into README.md.")
parser.add_argument("--dry-run", action="store_true", help="Compute metadata without writing files.")
parser.add_argument(
"--refresh-volatile",
action="store_true",
help="Refresh live star count and updated_at instead of preserving existing values.",
)
return parser.parse_args()
if __name__ == "__main__":
configure_utf8_output()
args = parse_args()
update_readme(dry_run=args.dry_run, refresh_volatile=args.refresh_volatile)