refactor: reorganize repo docs and tooling layout
Consolidate the repository into clearer apps, tools, and layered docs areas so contributors can navigate and maintain it more reliably. Align validation, metadata sync, and CI around the same canonical workflow to reduce drift across local checks and GitHub Actions.
This commit is contained in:
@@ -1,9 +1,16 @@
|
||||
#!/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: .*? -->")
|
||||
|
||||
|
||||
def configure_utf8_output() -> None:
|
||||
@@ -28,63 +35,227 @@ def configure_utf8_output() -> None:
|
||||
)
|
||||
|
||||
|
||||
def update_readme():
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
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 load_metadata(base_dir: str, repo: str = GITHUB_REPO) -> 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")
|
||||
|
||||
print(f"📖 Reading skills index from: {index_path}")
|
||||
with open(index_path, "r", encoding="utf-8") as f:
|
||||
skills = json.load(f)
|
||||
with open(index_path, "r", encoding="utf-8") as file:
|
||||
skills = json.load(file)
|
||||
|
||||
total_skills = len(skills)
|
||||
print(f"🔢 Total skills found: {total_skills}")
|
||||
with open(package_path, "r", encoding="utf-8") as file:
|
||||
package = json.load(file)
|
||||
|
||||
print(f"📝 Updating README at: {readme_path}")
|
||||
with open(readme_path, "r", encoding="utf-8") as f:
|
||||
content = f.read()
|
||||
with open(readme_path, "r", encoding="utf-8") as file:
|
||||
current_readme = file.read()
|
||||
|
||||
# 1. Update Title Count
|
||||
content = re.sub(
|
||||
r"(# 🌌 Antigravity Awesome Skills: )\d+(\+ Agentic Skills)",
|
||||
rf"\g<1>{total_skills}\g<2>",
|
||||
content,
|
||||
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)
|
||||
|
||||
live_stars = fetch_star_count(repo)
|
||||
total_stars = live_stars if live_stars is not None else current_stars or 0
|
||||
|
||||
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": datetime.now(timezone.utc).replace(microsecond=0).isoformat(),
|
||||
"used_live_star_count": live_stars is not None,
|
||||
}
|
||||
|
||||
|
||||
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']} -->"
|
||||
)
|
||||
|
||||
# 2. Update Blockquote Count
|
||||
content = re.sub(
|
||||
r"(Collection of )\d+(\+ Universal)",
|
||||
rf"\g<1>{total_skills}\g<2>",
|
||||
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,
|
||||
)
|
||||
|
||||
# 3. Update Intro Text Count
|
||||
content = re.sub(
|
||||
r"(library of \*\*)\d+( high-performance agentic skills\*\*)",
|
||||
rf"\g<1>{total_skills}\g<2>",
|
||||
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,
|
||||
)
|
||||
|
||||
# 4. Update Browse section header
|
||||
content = re.sub(
|
||||
r"## Browse \d+\+ Skills",
|
||||
f"## Browse {total_skills}+ Skills",
|
||||
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,
|
||||
)
|
||||
|
||||
# 5. Update TOC link for Browse (anchor matches header-derived slug)
|
||||
content = re.sub(
|
||||
r"\[📚 Browse \d+\+ Skills\]\(#browse-\d+-skills\)",
|
||||
f"[📚 Browse {total_skills}+ Skills](#browse-{total_skills}-skills)",
|
||||
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()}"
|
||||
|
||||
with open(readme_path, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write(content)
|
||||
|
||||
def update_readme(dry_run: 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)
|
||||
|
||||
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.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
configure_utf8_output()
|
||||
update_readme()
|
||||
args = parse_args()
|
||||
update_readme(dry_run=args.dry_run)
|
||||
|
||||
Reference in New Issue
Block a user