chore: remove duplicate scripts/ dir and clean .gitignore
- Delete scripts/sync_repo_metadata.py and scripts/update_readme.py (identical copies already in tools/scripts/, CI uses tools/scripts/) - Remove stale .gitignore entries: walkthrough.md, LOCAL_CONFIG.md - Update scripts/* patterns → tools/scripts/* in .gitignore - Stage previously-hidden docs/walkthrough.md and docs/users/walkthrough.md (were incorrectly excluded by the now-removed walkthrough.md rule)
This commit is contained in:
@@ -1,78 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from update_readme import configure_utf8_output, find_repo_root, load_metadata, update_readme
|
||||
|
||||
|
||||
ABOUT_DESCRIPTION_RE = re.compile(r'"description"\s*:\s*"([^"]*)"')
|
||||
|
||||
|
||||
def update_package_description(base_dir: str, metadata: dict, dry_run: bool) -> bool:
|
||||
package_path = os.path.join(base_dir, "package.json")
|
||||
with open(package_path, "r", encoding="utf-8") as file:
|
||||
content = file.read()
|
||||
|
||||
new_description = (
|
||||
f"{metadata['total_skills_label']} agentic skills for Claude Code, Gemini CLI, "
|
||||
"Cursor, Antigravity & more. Installer CLI."
|
||||
)
|
||||
updated_content = ABOUT_DESCRIPTION_RE.sub(
|
||||
f'"description": "{new_description}"', content, count=1
|
||||
)
|
||||
|
||||
if updated_content == content:
|
||||
return False
|
||||
|
||||
if dry_run:
|
||||
print(f"[dry-run] Would update package description in {package_path}")
|
||||
return True
|
||||
|
||||
with open(package_path, "w", encoding="utf-8", newline="\n") as file:
|
||||
file.write(updated_content)
|
||||
print(f"✅ Updated package description in {package_path}")
|
||||
return True
|
||||
|
||||
|
||||
def print_manual_github_about(metadata: dict) -> None:
|
||||
description = (
|
||||
f"{metadata['total_skills_label']} curated SKILL.md files for Claude Code, "
|
||||
"Cursor, Gemini CLI, Codex, Copilot, and Antigravity."
|
||||
)
|
||||
print("\nManual GitHub repo settings update:")
|
||||
print(f"- About description: {description}")
|
||||
print("- Suggested topics: claude-code, cursor, gemini-cli, codex-cli, github-copilot, antigravity")
|
||||
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Synchronize repository metadata across README and package.json."
|
||||
)
|
||||
parser.add_argument("--dry-run", action="store_true", help="Preview updates without writing files.")
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
args = parse_args()
|
||||
base_dir = find_repo_root(os.path.dirname(__file__))
|
||||
metadata = load_metadata(base_dir)
|
||||
|
||||
print("Repository metadata")
|
||||
print(json.dumps(metadata, indent=2))
|
||||
|
||||
readme_metadata = update_readme(dry_run=args.dry_run)
|
||||
package_updated = update_package_description(base_dir, metadata, args.dry_run)
|
||||
print_manual_github_about(readme_metadata)
|
||||
|
||||
if args.dry_run and not package_updated:
|
||||
print("\n[dry-run] No package.json description changes required.")
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
configure_utf8_output()
|
||||
sys.exit(main())
|
||||
@@ -1,261 +0,0 @@
|
||||
#!/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:
|
||||
"""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 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")
|
||||
|
||||
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()
|
||||
|
||||
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']} -->"
|
||||
)
|
||||
|
||||
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) -> 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()
|
||||
args = parse_args()
|
||||
update_readme(dry_run=args.dry_run)
|
||||
Reference in New Issue
Block a user