Files
antigravity-skills-reference/tools/scripts/sync_editorial_bundles.py
2026-03-27 08:51:59 +01:00

637 lines
21 KiB
Python

#!/usr/bin/env python3
from __future__ import annotations
import argparse
import json
import re
import shutil
from pathlib import Path
from typing import Any
from _project_paths import find_repo_root
from update_readme import configure_utf8_output, load_metadata
SAFE_SKILL_ID_RE = re.compile(
r"^(?!.*(?:^|/)\.{1,2}(?:/|$))[A-Za-z0-9._-]+(?:/[A-Za-z0-9._-]+)*$"
)
REPO_URL = "https://github.com/sickn33/antigravity-awesome-skills"
AUTHOR = {
"name": "sickn33 and contributors",
"url": REPO_URL,
}
ROOT_CLAUDE_PLUGIN_NAME = "antigravity-awesome-skills"
ROOT_CODEX_PLUGIN_NAME = "antigravity-awesome-skills"
EDITORIAL_BUNDLES_PATH = Path("data") / "editorial-bundles.json"
EDITORIAL_TEMPLATE_PATH = Path("tools") / "templates" / "editorial-bundles.md.tmpl"
CLAUDE_MARKETPLACE_PATH = Path(".claude-plugin") / "marketplace.json"
CLAUDE_PLUGIN_PATH = Path(".claude-plugin") / "plugin.json"
CODEX_MARKETPLACE_PATH = Path(".agents") / "plugins" / "marketplace.json"
CODEX_ROOT_PLUGIN_PATH = Path("plugins") / ROOT_CODEX_PLUGIN_NAME / ".codex-plugin" / "plugin.json"
ACRONYM_TOKENS = {
"ab": "A/B",
"adb": "ADB",
"adr": "ADR",
"ads": "ADS",
"ai": "AI",
"api": "API",
"apis": "APIs",
"app": "App",
"apps": "Apps",
"aso": "ASO",
"aws": "AWS",
"bat": "BAT",
"ci": "CI",
"cli": "CLI",
"cms": "CMS",
"crm": "CRM",
"cro": "CRO",
"css": "CSS",
"csv": "CSV",
"dag": "DAG",
"dbt": "dbt",
"ddd": "DDD",
"devops": "DevOps",
"docx": "DOCX",
"dx": "DX",
"e2e": "E2E",
"expo": "Expo",
"fastapi": "FastAPI",
"github": "GitHub",
"gitlab": "GitLab",
"grafana": "Grafana",
"html": "HTML",
"ios": "iOS",
"jwt": "JWT",
"k8s": "K8s",
"kpi": "KPI",
"langfuse": "Langfuse",
"langgraph": "LangGraph",
"linux": "Linux",
"llm": "LLM",
"llms": "LLMs",
"mcp": "MCP",
"nextjs": "Next.js",
"nodejs": "Node.js",
"oauth2": "OAuth2",
"odoo": "Odoo",
"openai": "OpenAI",
"owasp": "OWASP",
"pdf": "PDF",
"php": "PHP",
"postgres": "Postgres",
"pr": "PR",
"prd": "PRD",
"pwa": "PWA",
"python": "Python",
"rag": "RAG",
"rails": "Rails",
"react": "React",
"rest": "REST",
"rpc": "RPC",
"saas": "SaaS",
"seo": "SEO",
"shopify": "Shopify",
"slack": "Slack",
"slo": "SLO",
"sre": "SRE",
"sql": "SQL",
"sso": "SSO",
"stripe": "Stripe",
"svg": "SVG",
"swiftui": "SwiftUI",
"tailwind": "Tailwind",
"tdd": "TDD",
"ts": "TS",
"tsx": "TSX",
"ui": "UI",
"ux": "UX",
"uv": "uv",
"webgl": "WebGL",
"xcode": "Xcode",
"xml": "XML",
"yaml": "YAML",
"zod": "Zod",
}
def _read_json(path: Path) -> dict[str, Any]:
return json.loads(path.read_text(encoding="utf-8"))
def _write_json(path: Path, payload: dict[str, Any]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(json.dumps(payload, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
def _write_text(path: Path, content: str) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
path.write_text(content, encoding="utf-8", newline="\n")
def _clean_group_label(group: str) -> str:
return re.sub(r"^[^A-Za-z0-9]+", "", group).strip()
def _bundle_plugin_name(bundle_id: str) -> str:
return f"antigravity-bundle-{bundle_id}"
def _humanize_skill_label(skill_id: str) -> str:
tokens = re.split(r"[-_]+", skill_id.split("/")[-1])
words = [ACRONYM_TOKENS.get(token.lower(), token.capitalize()) for token in tokens if token]
return " ".join(words)
def _bundle_codex_long_description(bundle: dict[str, Any]) -> str:
audience = bundle.get("audience") or bundle["description"]
highlights = [
_humanize_skill_label(skill["id"])
for skill in bundle["skills"][:2]
if skill.get("id")
]
remaining = len(bundle["skills"]) - len(highlights)
if not highlights:
return f'{audience} Includes {len(bundle["skills"])} curated skills from Antigravity Awesome Skills.'
if remaining > 0:
return f"{audience} Covers {', '.join(highlights)}, and {remaining} more skills."
if len(highlights) == 1:
return f"{audience} Covers {highlights[0]}."
return f"{audience} Covers {' and '.join(highlights)}."
def _validate_bundle_skill_id(skill_id: str) -> None:
if not SAFE_SKILL_ID_RE.fullmatch(skill_id):
raise ValueError(f"Invalid skill id in editorial bundles manifest: {skill_id!r}")
def _validate_editorial_bundles(root: Path, payload: dict[str, Any]) -> list[dict[str, Any]]:
bundles = payload.get("bundles")
if not isinstance(bundles, list) or not bundles:
raise ValueError("data/editorial-bundles.json must contain a non-empty 'bundles' array.")
seen_bundle_ids: set[str] = set()
seen_bundle_names: set[str] = set()
skills_root = root / "skills"
for bundle in bundles:
if not isinstance(bundle, dict):
raise ValueError("Each editorial bundle must be an object.")
bundle_id = str(bundle.get("id", "")).strip()
bundle_name = str(bundle.get("name", "")).strip()
if not bundle_id or not bundle_name:
raise ValueError("Each editorial bundle requires non-empty 'id' and 'name'.")
if bundle_id in seen_bundle_ids:
raise ValueError(f"Duplicate editorial bundle id: {bundle_id}")
if bundle_name in seen_bundle_names:
raise ValueError(f"Duplicate editorial bundle name: {bundle_name}")
seen_bundle_ids.add(bundle_id)
seen_bundle_names.add(bundle_name)
plugin_name = _bundle_plugin_name(bundle_id)
if len(plugin_name) > 64:
raise ValueError(f"Bundle plugin name exceeds 64 characters: {plugin_name}")
for key in ("group", "emoji", "tagline", "audience", "description"):
if not str(bundle.get(key, "")).strip():
raise ValueError(f"Editorial bundle '{bundle_id}' is missing required field '{key}'.")
skills = bundle.get("skills")
if not isinstance(skills, list) or not skills:
raise ValueError(f"Editorial bundle '{bundle_id}' must include a non-empty 'skills' array.")
seen_skill_ids: set[str] = set()
for skill in skills:
if not isinstance(skill, dict):
raise ValueError(f"Editorial bundle '{bundle_id}' contains a non-object skill entry.")
skill_id = str(skill.get("id", "")).strip()
summary = str(skill.get("summary", "")).strip()
_validate_bundle_skill_id(skill_id)
if skill_id in seen_skill_ids:
raise ValueError(f"Editorial bundle '{bundle_id}' contains duplicate skill '{skill_id}'.")
if not summary:
raise ValueError(f"Editorial bundle '{bundle_id}' skill '{skill_id}' is missing summary.")
skill_path = (skills_root / skill_id).resolve(strict=False)
if not skill_path.exists():
raise ValueError(f"Editorial bundle '{bundle_id}' references missing skill '{skill_id}'.")
seen_skill_ids.add(skill_id)
return bundles
def _render_bundle_sections(bundles: list[dict[str, Any]]) -> str:
lines: list[str] = []
current_group: str | None = None
for bundle in bundles:
group = bundle["group"]
if group != current_group:
if lines:
lines.extend(["", "---", ""])
lines.append(f"## {group}")
lines.append("")
current_group = group
lines.append(f'### {bundle["emoji"]} {bundle["tagline"]}')
lines.append("")
lines.append(f'_{bundle["audience"]}_')
lines.append("")
for skill in bundle["skills"]:
lines.append(
f'- [`{skill["id"]}`](../../skills/{skill["id"]}/): {skill["summary"]}'
)
lines.append("")
return "\n".join(lines).strip() + "\n"
def render_bundles_doc(root: Path, metadata: dict[str, Any], bundles: list[dict[str, Any]]) -> str:
template = (root / EDITORIAL_TEMPLATE_PATH).read_text(encoding="utf-8")
return (
template.replace("{{bundle_sections}}", _render_bundle_sections(bundles).rstrip())
.replace("{{total_skills_label}}", metadata["total_skills_label"])
.replace("{{bundle_count}}", str(len(bundles)))
)
def _copy_file_contents(src: Path, dest: Path, allowed_root: Path) -> None:
resolved_src = src.resolve(strict=True)
resolved_src.relative_to(allowed_root.resolve())
if resolved_src.is_dir():
dest.mkdir(parents=True, exist_ok=True)
for child in resolved_src.iterdir():
_copy_file_contents(child, dest / child.name, allowed_root)
return
dest.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(resolved_src, dest)
def _copy_skill_directory(root: Path, skill_id: str, destination_root: Path) -> None:
skills_root = root / "skills"
source = (skills_root / skill_id).resolve(strict=True)
source.relative_to(skills_root.resolve())
if not source.is_dir():
raise ValueError(f"Editorial bundle skill '{skill_id}' is not a directory.")
skill_dest = destination_root / skill_id
if skill_dest.exists():
shutil.rmtree(skill_dest)
for child in source.iterdir():
_copy_file_contents(child, skill_dest / child.name, skills_root)
if not (skill_dest / "SKILL.md").is_file():
raise ValueError(f"Copied bundle skill '{skill_id}' is missing SKILL.md in {skill_dest}")
def _root_claude_plugin_manifest(metadata: dict[str, Any]) -> dict[str, Any]:
return {
"name": ROOT_CLAUDE_PLUGIN_NAME,
"version": metadata["version"],
"description": (
f"Universal agentic skill library for Claude Code with "
f"{metadata['total_skills_label']} reusable skills across coding, security, "
"design, product, and operations workflows."
),
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"claude-code",
"skills",
"agentic-skills",
"ai-coding",
"productivity",
],
}
def _root_codex_plugin_manifest(metadata: dict[str, Any]) -> dict[str, Any]:
return {
"name": ROOT_CODEX_PLUGIN_NAME,
"version": metadata["version"],
"description": "Repository-backed Codex plugin for the Antigravity Awesome Skills library.",
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"codex",
"skills",
"agentic-skills",
"developer-tools",
"productivity",
],
"skills": "./skills/",
"interface": {
"displayName": "Antigravity Awesome Skills",
"shortDescription": (
f'{metadata["total_skills_label"]} reusable skills for coding, security, '
"product, and ops workflows."
),
"longDescription": (
"Install the Antigravity Awesome Skills catalog as a Codex plugin and expose "
"the repository's curated skills library through a single marketplace entry."
),
"developerName": AUTHOR["name"],
"category": "Productivity",
"capabilities": ["Interactive", "Write"],
"websiteURL": REPO_URL,
"defaultPrompt": [
"Use @brainstorming to plan a new feature.",
"Use @test-driven-development to fix a bug safely.",
"Use @lint-and-validate to verify this branch.",
],
"brandColor": "#111827",
},
}
def _bundle_claude_plugin_manifest(metadata: dict[str, Any], bundle: dict[str, Any]) -> dict[str, Any]:
return {
"name": _bundle_plugin_name(bundle["id"]),
"version": metadata["version"],
"description": (
f'Editorial "{bundle["name"]}" bundle for Claude Code from Antigravity Awesome Skills.'
),
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"claude-code",
"skills",
"bundle",
bundle["id"],
"antigravity-awesome-skills",
],
}
def _bundle_codex_plugin_manifest(metadata: dict[str, Any], bundle: dict[str, Any]) -> dict[str, Any]:
category = _clean_group_label(bundle["group"])
plugin_name = _bundle_plugin_name(bundle["id"])
skill_count = len(bundle["skills"])
return {
"name": plugin_name,
"version": metadata["version"],
"description": (
f'Install the "{bundle["name"]}" editorial skill bundle from Antigravity Awesome Skills.'
),
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"codex",
"skills",
"bundle",
bundle["id"],
"productivity",
],
"skills": "./skills/",
"interface": {
"displayName": bundle["name"],
"shortDescription": f"{category} · {skill_count} curated skills",
"longDescription": _bundle_codex_long_description(bundle),
"developerName": AUTHOR["name"],
"category": category,
"capabilities": ["Interactive", "Write"],
"websiteURL": REPO_URL,
"brandColor": "#111827",
},
}
def _root_codex_plugin_manifest(metadata: dict[str, Any]) -> dict[str, Any]:
return {
"name": ROOT_CODEX_PLUGIN_NAME,
"version": metadata["version"],
"description": "Repository-backed Codex plugin for the Antigravity Awesome Skills library.",
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"codex",
"skills",
"agentic-skills",
"developer-tools",
"productivity",
],
"skills": "./skills/",
"interface": {
"displayName": "Antigravity Awesome Skills",
"shortDescription": (
f'{metadata["total_skills_label"]} reusable skills for coding, security, product, and ops workflows.'
),
"longDescription": (
"Install the Antigravity Awesome Skills catalog as a Codex plugin and expose "
"the repository's curated skills library through a single marketplace entry."
),
"developerName": AUTHOR["name"],
"category": "Productivity",
"capabilities": ["Interactive", "Write"],
"websiteURL": REPO_URL,
"defaultPrompt": [
"Use @brainstorming to plan a new feature.",
"Use @test-driven-development to fix a bug safely.",
"Use @lint-and-validate to verify this branch.",
],
"brandColor": "#111827",
},
}
def _bundle_claude_marketplace_entry(metadata: dict[str, Any], bundle: dict[str, Any]) -> dict[str, Any]:
plugin_name = _bundle_plugin_name(bundle["id"])
return {
"name": plugin_name,
"version": metadata["version"],
"description": (
f'Install the "{bundle["name"]}" editorial skill bundle for Claude Code.'
),
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"claude-code",
"skills",
"bundle",
bundle["id"],
"marketplace",
],
"source": f"./plugins/{plugin_name}",
}
def _render_claude_marketplace(metadata: dict[str, Any], bundles: list[dict[str, Any]]) -> dict[str, Any]:
plugins = [
{
"name": ROOT_CLAUDE_PLUGIN_NAME,
"version": metadata["version"],
"description": (
"Expose the full repository `skills/` tree to Claude Code through a "
"single marketplace entry."
),
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"claude-code",
"skills",
"agentic-skills",
"plugin",
"marketplace",
],
"source": "./",
}
]
plugins.extend(_bundle_claude_marketplace_entry(metadata, bundle) for bundle in bundles)
return {
"name": ROOT_CLAUDE_PLUGIN_NAME,
"owner": AUTHOR,
"metadata": {
"description": (
"Claude Code marketplace entries for the full Antigravity Awesome Skills "
"library and its editorial bundles."
),
"version": metadata["version"],
},
"plugins": plugins,
}
def _render_codex_marketplace(bundles: list[dict[str, Any]]) -> dict[str, Any]:
plugins: list[dict[str, Any]] = [
{
"name": ROOT_CODEX_PLUGIN_NAME,
"source": {
"source": "local",
"path": f"./plugins/{ROOT_CODEX_PLUGIN_NAME}",
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL",
},
"category": "Productivity",
}
]
for bundle in bundles:
plugins.append(
{
"name": _bundle_plugin_name(bundle["id"]),
"source": {
"source": "local",
"path": f'./plugins/{_bundle_plugin_name(bundle["id"])}',
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL",
},
"category": _clean_group_label(bundle["group"]),
}
)
return {
"name": ROOT_CODEX_PLUGIN_NAME,
"interface": {
"displayName": "Antigravity Awesome Skills",
},
"plugins": plugins,
}
def _sync_bundle_plugin_directory(root: Path, metadata: dict[str, Any], bundle: dict[str, Any]) -> None:
plugin_name = _bundle_plugin_name(bundle["id"])
plugin_root = root / "plugins" / plugin_name
if plugin_root.exists():
shutil.rmtree(plugin_root)
bundle_skills_root = plugin_root / "skills"
bundle_skills_root.mkdir(parents=True, exist_ok=True)
for skill in bundle["skills"]:
_copy_skill_directory(root, skill["id"], bundle_skills_root)
_write_json(
plugin_root / ".claude-plugin" / "plugin.json",
_bundle_claude_plugin_manifest(metadata, bundle),
)
_write_json(
plugin_root / ".codex-plugin" / "plugin.json",
_bundle_codex_plugin_manifest(metadata, bundle),
)
def sync_editorial_bundle_plugins(root: Path, metadata: dict[str, Any], bundles: list[dict[str, Any]]) -> None:
plugins_root = root / "plugins"
for candidate in plugins_root.glob("antigravity-bundle-*"):
if candidate.is_dir():
shutil.rmtree(candidate)
for bundle in bundles:
_sync_bundle_plugin_directory(root, metadata, bundle)
def load_editorial_bundles(root: Path) -> list[dict[str, Any]]:
root = Path(root)
payload = _read_json(root / EDITORIAL_BUNDLES_PATH)
return _validate_editorial_bundles(root, payload)
def sync_editorial_bundles(root: Path) -> None:
metadata = load_metadata(str(root))
bundles = load_editorial_bundles(root)
_write_text(root / "docs" / "users" / "bundles.md", render_bundles_doc(root, metadata, bundles))
_write_json(root / CLAUDE_PLUGIN_PATH, _root_claude_plugin_manifest(metadata))
_write_json(root / CLAUDE_MARKETPLACE_PATH, _render_claude_marketplace(metadata, bundles))
_write_json(root / CODEX_ROOT_PLUGIN_PATH, _root_codex_plugin_manifest(metadata))
_write_json(root / CODEX_MARKETPLACE_PATH, _render_codex_marketplace(bundles))
sync_editorial_bundle_plugins(root, metadata, bundles)
def parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Sync editorial bundle docs and plugin marketplaces.")
parser.add_argument(
"--check",
action="store_true",
help="Validate the editorial bundles manifest and exit without writing files.",
)
return parser.parse_args()
def main() -> int:
args = parse_args()
root = find_repo_root(__file__)
if args.check:
metadata = load_metadata(str(root))
bundles = load_editorial_bundles(root)
expected_doc = render_bundles_doc(root, metadata, bundles)
current_doc = (root / "docs" / "users" / "bundles.md").read_text(encoding="utf-8")
if current_doc != expected_doc:
raise SystemExit("docs/users/bundles.md is out of sync with data/editorial-bundles.json")
print("✅ Editorial bundles manifest and generated doc are in sync.")
return 0
sync_editorial_bundles(root)
print("✅ Editorial bundles synced.")
return 0
if __name__ == "__main__":
configure_utf8_output()
raise SystemExit(main())