diff --git a/.github/MAINTENANCE.md b/.github/MAINTENANCE.md index 548ce0be..9030daf1 100644 --- a/.github/MAINTENANCE.md +++ b/.github/MAINTENANCE.md @@ -80,7 +80,20 @@ Before ANY commit that adds/modifies skills, run the chain: npm run catalog ``` -3. **COMMIT GENERATED FILES**: +3. **Optional maintainer sweep shortcut**: + ```bash + npm run sync:repo-state + ``` + This wraps `chain + catalog + sync:contributors + audit:consistency` for a full local repo-state refresh. + + When you need the live GitHub repo metadata updated too, run: + + ```bash + npm run sync:github-about + npm run audit:consistency:github + ``` + +4. **COMMIT GENERATED FILES**: ```bash git add README.md skills_index.json data/skills_index.json data/catalog.json data/bundles.json data/aliases.json CATALOG.md git commit -m "chore: sync generated files" @@ -180,8 +193,9 @@ We used this flow for PRs [#220](https://github.com/sickn33/antigravity-awesome- After you have merged several PRs or before cutting a release: 1. **Sync Contributors List**: - - Run: `git shortlog -sn --all` - - Update `## Repo Contributors` in README.md. + - Run: `npm run sync:contributors` + - This refreshes `## Repo Contributors` in `README.md` from the live GitHub contributor list while preserving custom bot/app links. + - If you are already doing a full maintainer sweep, prefer `npm run sync:repo-state`. 2. **Verify Table of Contents**: - Ensure all new headers have clean anchors. diff --git a/CHANGELOG.md b/CHANGELOG.md index 704f56d9..7eb3d062 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Realigned README, package metadata, user docs, and GitHub About guidance to the current `1,304+` catalog state and `v8.4.0` release copy. - Automated metadata propagation for curated docs and package copy so `npm run chain` now keeps README, package description, and the main count-sensitive user/maintainer docs aligned when the skill catalog changes. - Added an explicit `sync:github-about` automation path so GitHub About description, homepage, and topics can be refreshed from the same metadata source instead of being updated manually. +- Added contributor sync plus repo-state audit automation: `sync:contributors`, `check:stale-claims`, `audit:consistency`, and `sync:repo-state` now cover contributor acknowledgements, stale count/version drift, and end-to-end maintainer sanity checks. ## [8.4.0] - 2026-03-20 - "Discovery, Metadata, and Release Hardening" diff --git a/README.md b/README.md index 840a4fe8..2974a7d6 100644 --- a/README.md +++ b/README.md @@ -521,11 +521,6 @@ Made with [contrib.rocks](https://contrib.rocks). *(Image may be cached; [view l We officially thank the following contributors for their help in making this repository awesome! -- [@JayeHarrill](https://github.com/JayeHarrill) -- [@wd041216-bit](https://github.com/wd041216-bit) -- [@Champbreed](https://github.com/Champbreed) -- [@suhaibjanjua](https://github.com/suhaibjanjua) -- [@Wolfe-Jam](https://github.com/Wolfe-Jam) - [@sck000](https://github.com/sck000) - [@github-actions[bot]](https://github.com/apps/github-actions) - [@sickn33](https://github.com/sickn33) @@ -541,6 +536,8 @@ We officially thank the following contributors for their help in making this rep - [@itsmeares](https://github.com/itsmeares) - [@chauey](https://github.com/chauey) - [@ar27111994](https://github.com/ar27111994) +- [@suhaibjanjua](https://github.com/suhaibjanjua) +- [@Champbreed](https://github.com/Champbreed) - [@GuppyTheCat](https://github.com/GuppyTheCat) - [@Copilot](https://github.com/apps/copilot-swe-agent) - [@8hrsk](https://github.com/8hrsk) @@ -549,17 +546,23 @@ We officially thank the following contributors for their help in making this rep - [@0xrohitgarg](https://github.com/0xrohitgarg) - [@zebbern](https://github.com/zebbern) - [@talesperito](https://github.com/talesperito) +- [@fernandorych](https://github.com/fernandorych) - [@SnakeEye-sudo](https://github.com/SnakeEye-sudo) - [@nikolasdehor](https://github.com/nikolasdehor) -- [@fernandorych](https://github.com/fernandorych) -- [@taksrules](https://github.com/taksrules) - [@jackjin1997](https://github.com/jackjin1997) - [@HuynhNhatKhanh](https://github.com/HuynhNhatKhanh) +- [@taksrules](https://github.com/taksrules) - [@liyin2015](https://github.com/liyin2015) +- [@Gizzant](https://github.com/Gizzant) +- [@AssassinMaeve](https://github.com/AssassinMaeve) +- [@Musayrlsms](https://github.com/Musayrlsms) - [@arathiesh](https://github.com/arathiesh) - [@Tiger-Foxx](https://github.com/Tiger-Foxx) - [@RamonRiosJr](https://github.com/RamonRiosJr) -- [@Musayrlsms](https://github.com/Musayrlsms) +- [@ziuus](https://github.com/ziuus) +- [@Wolfe-Jam](https://github.com/Wolfe-Jam) +- [@Wittlesus](https://github.com/Wittlesus) +- [@wahidzzz](https://github.com/wahidzzz) - [@Vonfry](https://github.com/Vonfry) - [@vprudnikoff](https://github.com/vprudnikoff) - [@viktor-ferenczi](https://github.com/viktor-ferenczi) @@ -581,12 +584,17 @@ We officially thank the following contributors for their help in making this rep - [@yubing744](https://github.com/yubing744) - [@vuth-dogo](https://github.com/vuth-dogo) - [@yang1002378395-cmyk](https://github.com/yang1002378395-cmyk) +- [@viliawang-pm](https://github.com/viliawang-pm) +- [@tsilverberg](https://github.com/tsilverberg) - [@thuanlm215](https://github.com/thuanlm215) - [@shmlkv](https://github.com/shmlkv) - [@rafsilva85](https://github.com/rafsilva85) - [@nocodemf](https://github.com/nocodemf) +- [@marsiandeployer](https://github.com/marsiandeployer) +- [@ksgisang](https://github.com/ksgisang) - [@KrisnaSantosa15](https://github.com/KrisnaSantosa15) - [@junited31](https://github.com/junited31) +- [@fullstackcrew-alpha](https://github.com/fullstackcrew-alpha) - [@fbientrigo](https://github.com/fbientrigo) - [@dz3ai](https://github.com/dz3ai) - [@developer-victor](https://github.com/developer-victor) @@ -594,27 +602,24 @@ We officially thank the following contributors for their help in making this rep - [@christopherlhammer11-ai](https://github.com/christopherlhammer11-ai) - [@c1c3ru](https://github.com/c1c3ru) - [@buzzbysolcex](https://github.com/buzzbysolcex) +- [@BenZinaDaze](https://github.com/BenZinaDaze) - [@avimak](https://github.com/avimak) - [@antbotlab](https://github.com/antbotlab) - [@amalsam](https://github.com/amalsam) -- [@ziuus](https://github.com/ziuus) -- [@Wittlesus](https://github.com/Wittlesus) -- [@wahidzzz](https://github.com/wahidzzz) -- [@olgasafonova](https://github.com/olgasafonova) +- [@qcwssss](https://github.com/qcwssss) +- [@rcigor](https://github.com/rcigor) - [@hvasconcelos](https://github.com/hvasconcelos) - [@Guilherme-ruy](https://github.com/Guilherme-ruy) -- [@Gizzant](https://github.com/Gizzant) - [@Digidai](https://github.com/Digidai) - [@dbhat93](https://github.com/dbhat93) -- [@BenZinaDaze](https://github.com/BenZinaDaze) -- [@JaskiratAnand](https://github.com/JaskiratAnand) -- [@marsiandeployer](https://github.com/marsiandeployer) -- [@suhaibjanjua](https://github.com/suhaibjanjua) - [@decentraliser](https://github.com/decentraliser) - [@MAIOStudio](https://github.com/MAIOStudio) +- [@wd041216-bit](https://github.com/wd041216-bit) - [@conorbronsdon](https://github.com/conorbronsdon) +- [@ChaosRealmsAI](https://github.com/ChaosRealmsAI) - [@kriptoburak](https://github.com/kriptoburak) - [@BenedictKing](https://github.com/BenedictKing) +- [@fernandezbaptiste](https://github.com/fernandezbaptiste) - [@acbhatt12](https://github.com/acbhatt12) - [@Andruia](https://github.com/Andruia) - [@AlmogBaku](https://github.com/AlmogBaku) @@ -625,15 +630,19 @@ We officially thank the following contributors for their help in making this rep - [@ALEKGG1](https://github.com/ALEKGG1) - [@8144225309](https://github.com/8144225309) - [@1bcMax](https://github.com/1bcMax) +- [@olgasafonova](https://github.com/olgasafonova) - [@sharmanilay](https://github.com/sharmanilay) - [@KhaiTrang1995](https://github.com/KhaiTrang1995) - [@LocNguyenSGU](https://github.com/LocNguyenSGU) - [@nedcodes-ok](https://github.com/nedcodes-ok) - [@iftikharg786](https://github.com/iftikharg786) +- [@halith-smh](https://github.com/halith-smh) - [@mertbaskurt](https://github.com/mertbaskurt) - [@MatheusCampagnolo](https://github.com/MatheusCampagnolo) +- [@Marvin19700118](https://github.com/Marvin19700118) - [@djmahe4](https://github.com/djmahe4) - [@MArbeeGit](https://github.com/MArbeeGit) +- [@majorelalexis-stack](https://github.com/majorelalexis-stack) - [@Svobikl](https://github.com/Svobikl) - [@kromahlusenii-ops](https://github.com/kromahlusenii-ops) - [@Krishna-Modi12](https://github.com/Krishna-Modi12) @@ -643,17 +652,8 @@ We officially thank the following contributors for their help in making this rep - [@kage-art](https://github.com/kage-art) - [@whatiskadudoing](https://github.com/whatiskadudoing) - [@jonathimer](https://github.com/jonathimer) -- [@qcwssss](https://github.com/qcwssss) -- [@rcigor](https://github.com/rcigor) -- [@tsilverberg](https://github.com/tsilverberg) -- [@Marvin19700118](https://github.com/Marvin19700118) -- [@ChaosRealmsAI](https://github.com/ChaosRealmsAI) -- [@ksgisang](https://github.com/ksgisang) -- [@viliawang-pm](https://github.com/viliawang-pm) -- [@AssassinMaeve](https://github.com/AssassinMaeve) -- [@fernandezbaptiste](https://github.com/fernandezbaptiste) - ---- +- [@JayeHarrill](https://github.com/JayeHarrill) +- [@JaskiratAnand](https://github.com/JaskiratAnand) ## License diff --git a/package.json b/package.json index 213881d9..0559ec50 100644 --- a/package.json +++ b/package.json @@ -17,10 +17,16 @@ "readme": "node tools/scripts/run-python.js tools/scripts/update_readme.py", "sync:metadata": "node tools/scripts/run-python.js tools/scripts/sync_repo_metadata.py", "sync:github-about": "node tools/scripts/run-python.js tools/scripts/sync_repo_metadata.py --apply-github-about", + "sync:contributors": "node tools/scripts/run-python.js tools/scripts/sync_contributors.py", "chain": "npm run validate && npm run index && npm run sync:metadata", "sync:all": "npm run chain", + "sync:repo-state": "npm run chain && npm run catalog && npm run sync:contributors && npm run audit:consistency", + "sync:repo-state:full": "npm run sync:repo-state && npm run sync:github-about && npm run audit:consistency:github", "catalog": "node tools/scripts/build-catalog.js", "build": "npm run chain && npm run catalog", + "check:stale-claims": "node tools/scripts/run-python.js tools/scripts/check_stale_claims.py", + "audit:consistency": "node tools/scripts/run-python.js tools/scripts/audit_consistency.py", + "audit:consistency:github": "node tools/scripts/run-python.js tools/scripts/audit_consistency.py --check-github-about", "security:docs": "node tools/scripts/tests/docs_security_content.test.js", "pr:preflight": "node tools/scripts/pr_preflight.js", "release:preflight": "node tools/scripts/release_workflow.js preflight", diff --git a/tools/scripts/audit_consistency.py b/tools/scripts/audit_consistency.py new file mode 100644 index 00000000..cd48c3a7 --- /dev/null +++ b/tools/scripts/audit_consistency.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import json +import subprocess +import sys +from pathlib import Path + +from _project_paths import find_repo_root +import sync_repo_metadata +from update_readme import configure_utf8_output, load_metadata, apply_metadata + + +def _read_text(path: Path) -> str: + return path.read_text(encoding="utf-8") + + +def _package_expected_description(metadata: dict) -> str: + return ( + f"{metadata['total_skills_label']} agentic skills for Claude Code, Gemini CLI, " + "Cursor, Antigravity & more. Installer CLI." + ) + + +def _expected_readme(content: str, metadata: dict) -> str: + return sync_repo_metadata.sync_readme_copy(apply_metadata(content, metadata), metadata) + + +def _expected_getting_started(content: str, metadata: dict) -> str: + return sync_repo_metadata.sync_getting_started(content, metadata) + + +def _expected_bundles(content: str, metadata: dict) -> str: + return sync_repo_metadata.sync_bundles_doc(content, metadata) + + +def _expected_regex_sync(content: str, replacements: list[tuple[str, str]]) -> str: + return sync_repo_metadata.sync_regex_text(content, replacements) + + +def _expected_jetski_cortex(content: str, metadata: dict) -> str: + return sync_repo_metadata.sync_jetski_cortex(content, metadata) + + +def find_local_consistency_issues(base_dir: str | Path) -> list[str]: + root = Path(base_dir) + metadata = load_metadata(str(root)) + issues: list[str] = [] + + package_json = json.loads(_read_text(root / "package.json")) + if package_json.get("description") != _package_expected_description(metadata): + issues.append("package.json description is out of sync with the live skills count") + + file_checks = [ + ("README.md", _expected_readme), + ("docs/users/getting-started.md", _expected_getting_started), + ("docs/users/bundles.md", _expected_bundles), + ("docs/integrations/jetski-cortex.md", _expected_jetski_cortex), + ( + "docs/users/claude-code-skills.md", + lambda content, current_metadata: _expected_regex_sync( + content, + [(r"\d[\d,]*\+ skills", f"{current_metadata['total_skills_label']} skills")], + ), + ), + ( + "docs/users/gemini-cli-skills.md", + lambda content, current_metadata: _expected_regex_sync( + content, + [(r"\d[\d,]*\+ files", f"{current_metadata['total_skills_label']} files")], + ), + ), + ( + "docs/users/usage.md", + lambda content, current_metadata: _expected_regex_sync( + content, + [ + (r"\d[\d,]*\+ skill files", f"{current_metadata['total_skills_label']} skill files"), + (r"\d[\d,]*\+ tools", f"{current_metadata['total_skills_label']} tools"), + (r"all \d[\d,]*\+ skills", f"all {current_metadata['total_skills_label']} skills"), + ( + r"have \d[\d,]*\+ skills installed locally", + f"have {current_metadata['total_skills_label']} skills installed locally", + ), + ], + ), + ), + ( + "docs/users/visual-guide.md", + lambda content, current_metadata: _expected_regex_sync( + content, + [ + (r"\d[\d,]*\+ skills live here", f"{current_metadata['total_skills_label']} skills live here"), + (r"\d[\d,]*\+ total", f"{current_metadata['total_skills_label']} total"), + (r"\d[\d,]*\+ SKILLS", f"{current_metadata['total_skills_label']} SKILLS"), + ], + ), + ), + ( + "docs/users/kiro-integration.md", + lambda content, current_metadata: _expected_regex_sync( + content, + [(r"\d[\d,]*\+ specialized areas", f"{current_metadata['total_skills_label']} specialized areas")], + ), + ), + ( + "docs/maintainers/repo-growth-seo.md", + lambda content, current_metadata: _expected_regex_sync( + content, + [ + (r"\d[\d,]*\+ agentic skills", f"{current_metadata['total_skills_label']} agentic skills"), + (r"\d[\d,]*\+ Agentic Skills", f"{current_metadata['total_skills_label']} Agentic Skills"), + ], + ), + ), + ( + "docs/maintainers/skills-update-guide.md", + lambda content, current_metadata: _expected_regex_sync( + content, + [ + ( + r"All \d[\d,]*\+ skills from the skills directory", + f"All {current_metadata['total_skills_label']} skills from the skills directory", + ) + ], + ), + ), + ( + "docs/integrations/jetski-gemini-loader/README.md", + lambda content, current_metadata: _expected_regex_sync( + content, + [(r"\d[\d,]*\+ skills", f"{current_metadata['total_skills_label']} skills")], + ), + ), + ] + + for relative_path, transform in file_checks: + path = root / relative_path + if not path.is_file(): + issues.append(f"{relative_path} is missing") + continue + original = _read_text(path) + expected = transform(original, metadata) + if original != expected: + issues.append(f"{relative_path} contains stale or inconsistent generated claims") + + return issues + + +def find_github_about_issues(base_dir: str | Path) -> list[str]: + root = Path(base_dir) + metadata = load_metadata(str(root)) + result = subprocess.run( + [ + "gh", + "repo", + "view", + metadata["repo"], + "--json", + "description,homepageUrl,repositoryTopics", + ], + check=True, + capture_output=True, + text=True, + ) + payload = json.loads(result.stdout) + issues: list[str] = [] + + if payload.get("description") != sync_repo_metadata.build_about_description(metadata): + issues.append("GitHub About description is out of sync") + if payload.get("homepageUrl") != sync_repo_metadata.GITHUB_HOMEPAGE_URL: + issues.append("GitHub About homepage is out of sync") + + current_topics = sorted( + entry["name"] for entry in payload.get("repositoryTopics", []) if isinstance(entry, dict) and "name" in entry + ) + expected_topics = sorted(sync_repo_metadata.build_about_topics()) + if current_topics != expected_topics: + issues.append("GitHub About topics are out of sync") + + return issues + + +def parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser(description="Audit repository consistency for generated claims.") + parser.add_argument( + "--check-github-about", + action="store_true", + help="Also verify the live GitHub About description, homepage, and topics via gh CLI.", + ) + return parser.parse_args() + + +def main() -> int: + args = parse_args() + root = find_repo_root(__file__) + + issues = find_local_consistency_issues(root) + if args.check_github_about: + issues.extend(find_github_about_issues(root)) + + if issues: + for issue in issues: + print(f"❌ {issue}") + return 1 + + print("✅ Repository consistency audit passed.") + return 0 + + +if __name__ == "__main__": + configure_utf8_output() + sys.exit(main()) diff --git a/tools/scripts/check_stale_claims.py b/tools/scripts/check_stale_claims.py new file mode 100644 index 00000000..e103da2a --- /dev/null +++ b/tools/scripts/check_stale_claims.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import sys + +from _project_paths import find_repo_root +from audit_consistency import find_local_consistency_issues +from update_readme import configure_utf8_output + + +def main() -> int: + root = find_repo_root(__file__) + issues = find_local_consistency_issues(root) + if issues: + for issue in issues: + print(f"❌ {issue}") + return 1 + + print("✅ No stale claims detected in active docs.") + return 0 + + +if __name__ == "__main__": + configure_utf8_output() + sys.exit(main()) diff --git a/tools/scripts/sync_contributors.py b/tools/scripts/sync_contributors.py new file mode 100644 index 00000000..d920e093 --- /dev/null +++ b/tools/scripts/sync_contributors.py @@ -0,0 +1,133 @@ +#!/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_START = "We officially thank the following contributors for their help in making this repository awesome!\n\n" +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