Files
Nick a07936c989 chore: Rename project to Agentic Awesome Skills
Remove the old first-party Antigravity Awesome Skills identity from package metadata, docs, web app SEO surfaces, generated plugin metadata, and installer references.

Preserve truthful Antigravity compatibility and external source provenance references while adding independent-project disclaimers.
2026-07-08 22:02:03 +02:00

172 lines
5.7 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import re
import subprocess
import sys
from pathlib import Path
from _project_paths import find_repo_root
from update_readme import configure_utf8_output, load_metadata
CONTRIBUTOR_SECTION_HEADING = "## Repo Contributors"
CONTRIBUTOR_SECTION_START = "We officially thank the following contributors for their help in making this repository awesome!\n\n"
CONTRIB_ROCKS_MAX = 500
SPECIAL_LINK_OVERRIDES = {
"Copilot": "https://github.com/apps/copilot-swe-agent",
"github-actions[bot]": "https://github.com/apps/github-actions",
"copilot-swe-agent[bot]": "https://github.com/apps/copilot-swe-agent",
}
def parse_existing_contributor_links(content: str) -> dict[str, str]:
links: dict[str, str] = {}
pattern = re.compile(r"^- \[@(?P<label>.+?)\]\((?P<url>https://github\.com/.+?)\)$")
for line in content.splitlines():
match = pattern.match(line.strip())
if not match:
continue
links[match.group("label")] = match.group("url")
return links
def parse_existing_contributor_order(content: str) -> list[str]:
order: list[str] = []
seen: set[str] = set()
pattern = re.compile(r"^- \[@(?P<label>.+?)\]\((?P<url>https://github\.com/.+?)\)$")
for line in content.splitlines():
match = pattern.match(line.strip())
if not match:
continue
label = match.group("label")
if label in seen:
continue
seen.add(label)
order.append(label)
return order
def parse_contributors_response(payload: list[dict]) -> list[str]:
contributors: list[str] = []
seen: set[str] = set()
for entry in payload:
login = entry.get("login")
if not isinstance(login, str) or not login or login in seen:
continue
seen.add(login)
contributors.append(login)
return contributors
def order_contributors_for_render(contributors: list[str], existing_order: list[str]) -> list[str]:
contributor_set = set(contributors)
ordered_existing = [login for login in existing_order if login in contributor_set]
new_contributors = sorted(
(login for login in contributors if login not in existing_order),
key=lambda login: login.casefold(),
)
return ordered_existing + new_contributors
def infer_contributor_url(login: str, existing_links: dict[str, str]) -> str:
if login in existing_links:
return existing_links[login]
if login in SPECIAL_LINK_OVERRIDES:
return SPECIAL_LINK_OVERRIDES[login]
if login.endswith("[bot]"):
app_name = login[: -len("[bot]")]
return f"https://github.com/apps/{app_name}"
return f"https://github.com/{login}"
def render_contributor_lines(contributors: list[str], existing_links: dict[str, str]) -> str:
lines = []
for login in contributors:
url = infer_contributor_url(login, existing_links)
lines.append(f"- [@{login}]({url})")
return "\n".join(lines)
def render_repo_contributors_section(repo: str) -> str:
return f"""{CONTRIBUTOR_SECTION_HEADING}
<a href="https://github.com/{repo}/graphs/contributors">
<img src="https://contrib.rocks/image?repo={repo}&max={CONTRIB_ROCKS_MAX}" alt="Repository contributors" />
</a>
Made with [contrib.rocks](https://contrib.rocks). *(Image may be cached; [view live contributors](https://github.com/{repo}/graphs/contributors) on GitHub.)*
{CONTRIBUTOR_SECTION_START}
"""
def update_repo_contributors_section(content: str, contributors: list[str], repo: str = "sickn33/agentic-awesome-skills") -> str:
if CONTRIBUTOR_SECTION_HEADING not in content or "\n## " not in content:
raise ValueError("README.md does not contain the expected Repo Contributors section structure.")
start_index = content.index(CONTRIBUTOR_SECTION_HEADING)
end_index = content.index("\n## ", start_index + len(CONTRIBUTOR_SECTION_HEADING))
return f"{content[:start_index]}{render_repo_contributors_section(repo).rstrip()}\n{content[end_index:]}"
def fetch_contributors(repo: str) -> list[str]:
result = subprocess.run(
[
"gh",
"api",
f"repos/{repo}/contributors?per_page=100",
"--paginate",
"--slurp",
],
check=True,
capture_output=True,
text=True,
)
payload = json.loads(result.stdout)
flat_entries: list[dict] = []
for page in payload:
if isinstance(page, list):
flat_entries.extend(entry for entry in page if isinstance(entry, dict))
return parse_contributors_response(flat_entries)
def sync_contributors(base_dir: str | Path, dry_run: bool = False) -> bool:
root = Path(base_dir)
metadata = load_metadata(str(root))
contributors = fetch_contributors(metadata["repo"])
readme_path = root / "README.md"
original = readme_path.read_text(encoding="utf-8")
updated = update_repo_contributors_section(original, contributors, metadata["repo"])
if updated == original:
return False
if dry_run:
print(f"[dry-run] Would update contributors in {readme_path}")
return True
readme_path.write_text(updated, encoding="utf-8", newline="\n")
print(f"✅ Updated contributors in {readme_path}")
return True
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Synchronize the README Repo Contributors section.")
parser.add_argument("--dry-run", action="store_true", help="Preview contributor changes without writing files.")
return parser.parse_args()
def main() -> int:
args = parse_args()
root = find_repo_root(__file__)
sync_contributors(root, dry_run=args.dry_run)
return 0
if __name__ == "__main__":
configure_utf8_output()
sys.exit(main())