Files
antigravity-skills-reference/tools/scripts/sync_editorial_bundles.py

737 lines
24 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 plugin_compatibility import build_report as build_plugin_compatibility_report
from plugin_compatibility import compatibility_by_skill_id, sync_plugin_compatibility
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"
ROOT_CLAUDE_PLUGIN_DIRNAME = "antigravity-awesome-skills-claude"
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"
CLAUDE_ROOT_PLUGIN_PATH = Path("plugins") / ROOT_CLAUDE_PLUGIN_DIRNAME / ".claude-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 _format_count_label(count: int) -> str:
return f"{count:,}"
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 _bundle_target_status(bundle: dict[str, Any], compatibility: dict[str, dict[str, Any]]) -> dict[str, Any]:
bundle_skills = [compatibility[skill["id"]] for skill in bundle["skills"] if skill["id"] in compatibility]
return {
"codex": bool(bundle_skills) and all(skill["targets"]["codex"] == "supported" for skill in bundle_skills),
"claude": bool(bundle_skills) and all(skill["targets"]["claude"] == "supported" for skill in bundle_skills),
"manual_setup": any(skill["setup"]["type"] == "manual" for skill in bundle_skills),
}
def _render_bundle_plugin_status(bundle_status: dict[str, Any]) -> str:
codex_status = "Codex plugin-safe" if bundle_status["codex"] else "Codex pending hardening"
claude_status = "Claude plugin-safe" if bundle_status["claude"] else "Claude pending hardening"
parts = [codex_status, claude_status]
if bundle_status["manual_setup"]:
parts.append("Requires manual setup")
return " · ".join(parts)
def _render_bundle_sections(
bundles: list[dict[str, Any]],
compatibility: dict[str, 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
bundle_status = _bundle_target_status(bundle, compatibility)
lines.append(f'### {bundle["emoji"]} {bundle["tagline"]}')
lines.append("")
lines.append(f'_{bundle["audience"]}_')
lines.append("")
lines.append(f'**Plugin status:** {_render_bundle_plugin_status(bundle_status)}')
lines.append("")
for skill in bundle["skills"]:
skill_status = compatibility.get(skill["id"], {})
plugin_info = skill_status.get("setup", {}) if isinstance(skill_status, dict) else {}
suffix = " _(manual setup)_" if plugin_info.get("type") == "manual" else ""
lines.append(
f'- [`{skill["id"]}`](../../skills/{skill["id"]}/): {skill["summary"]}{suffix}'
)
lines.append("")
return "\n".join(lines).strip() + "\n"
def render_bundles_doc(
root: Path,
metadata: dict[str, Any],
bundles: list[dict[str, Any]],
compatibility: dict[str, dict[str, Any]],
) -> str:
template = (root / EDITORIAL_TEMPLATE_PATH).read_text(encoding="utf-8")
return (
template.replace("{{bundle_sections}}", _render_bundle_sections(bundles, compatibility).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], supported_skill_count: int) -> dict[str, Any]:
supported_label = _format_count_label(supported_skill_count)
return {
"name": ROOT_CLAUDE_PLUGIN_NAME,
"version": metadata["version"],
"description": (
f"Plugin-safe Claude Code distribution of Antigravity Awesome Skills with "
f"{supported_label} supported skills."
),
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"claude-code",
"skills",
"agentic-skills",
"plugin-safe",
"productivity",
],
}
def _root_codex_plugin_manifest(metadata: dict[str, Any], supported_skill_count: int) -> dict[str, Any]:
supported_label = _format_count_label(supported_skill_count)
return {
"name": ROOT_CODEX_PLUGIN_NAME,
"version": metadata["version"],
"description": "Plugin-safe 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",
"plugin-safe",
],
"skills": "./skills/",
"interface": {
"displayName": "Antigravity Awesome Skills",
"shortDescription": (
f"{supported_label} plugin-safe skills for coding, security, product, and ops workflows."
),
"longDescription": (
"Install a plugin-safe Codex distribution of Antigravity Awesome Skills. "
"Skills that still need hardening or target-specific setup remain available in the repo "
"but are excluded from this plugin."
),
"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 _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]],
bundle_support: dict[str, dict[str, Any]],
) -> dict[str, Any]:
plugins = [
{
"name": ROOT_CLAUDE_PLUGIN_NAME,
"version": metadata["version"],
"description": (
"Expose the plugin-safe Claude Code subset of Antigravity Awesome Skills "
"through a single marketplace entry."
),
"author": AUTHOR,
"homepage": REPO_URL,
"repository": REPO_URL,
"license": "MIT",
"keywords": [
"claude-code",
"skills",
"agentic-skills",
"plugin",
"marketplace",
],
"source": f"./plugins/{ROOT_CLAUDE_PLUGIN_DIRNAME}",
}
]
plugins.extend(
_bundle_claude_marketplace_entry(metadata, bundle)
for bundle in bundles
if bundle_support[bundle["id"]]["claude"]
)
return {
"name": ROOT_CLAUDE_PLUGIN_NAME,
"owner": AUTHOR,
"metadata": {
"description": (
"Claude Code marketplace entries for the plugin-safe Antigravity Awesome Skills "
"library and its compatible editorial bundles."
),
"version": metadata["version"],
},
"plugins": plugins,
}
def _render_codex_marketplace(
bundles: list[dict[str, Any]],
bundle_support: dict[str, 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:
if not bundle_support[bundle["id"]]["codex"]:
continue
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 _materialize_plugin_skills(root: Path, destination_root: Path, skill_ids: list[str]) -> None:
if destination_root.is_symlink() or destination_root.is_file():
destination_root.unlink()
elif destination_root.exists():
shutil.rmtree(destination_root)
destination_root.mkdir(parents=True, exist_ok=True)
for skill_id in skill_ids:
_copy_skill_directory(root, skill_id, destination_root)
def _supported_skill_ids(
compatibility: dict[str, dict[str, Any]],
target: str,
) -> list[str]:
return sorted(
skill_id
for skill_id, skill in compatibility.items()
if skill["targets"][target] == "supported"
)
def _sync_root_plugins(
root: Path,
metadata: dict[str, Any],
compatibility: dict[str, dict[str, Any]],
) -> None:
codex_skill_ids = _supported_skill_ids(compatibility, "codex")
claude_skill_ids = _supported_skill_ids(compatibility, "claude")
codex_root = root / "plugins" / ROOT_CODEX_PLUGIN_NAME
claude_root = root / "plugins" / ROOT_CLAUDE_PLUGIN_DIRNAME
_materialize_plugin_skills(root, codex_root / "skills", codex_skill_ids)
_materialize_plugin_skills(root, claude_root / "skills", claude_skill_ids)
_write_json(
codex_root / ".codex-plugin" / "plugin.json",
_root_codex_plugin_manifest(metadata, len(codex_skill_ids)),
)
claude_manifest = _root_claude_plugin_manifest(metadata, len(claude_skill_ids))
_write_json(
claude_root / ".claude-plugin" / "plugin.json",
claude_manifest,
)
_write_json(root / CLAUDE_PLUGIN_PATH, claude_manifest)
def _sync_bundle_plugin_directory(
root: Path,
metadata: dict[str, Any],
bundle: dict[str, Any],
support: dict[str, Any],
) -> None:
if not support["codex"] and not support["claude"]:
return
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)
if support["claude"]:
_write_json(
plugin_root / ".claude-plugin" / "plugin.json",
_bundle_claude_plugin_manifest(metadata, bundle),
)
if support["codex"]:
_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]],
bundle_support: dict[str, 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, bundle_support[bundle["id"]])
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))
compatibility_report = sync_plugin_compatibility(root)
compatibility = compatibility_by_skill_id(compatibility_report)
bundles = load_editorial_bundles(root)
bundle_support = {
bundle["id"]: _bundle_target_status(bundle, compatibility)
for bundle in bundles
}
_write_text(
root / "docs" / "users" / "bundles.md",
render_bundles_doc(root, metadata, bundles, compatibility),
)
_sync_root_plugins(root, metadata, compatibility)
_write_json(
root / CLAUDE_MARKETPLACE_PATH,
_render_claude_marketplace(metadata, bundles, bundle_support),
)
_write_json(
root / CODEX_MARKETPLACE_PATH,
_render_codex_marketplace(bundles, bundle_support),
)
_write_json(
root / CODEX_ROOT_PLUGIN_PATH,
_root_codex_plugin_manifest(
metadata,
len(_supported_skill_ids(compatibility, "codex")),
),
)
sync_editorial_bundle_plugins(root, metadata, bundles, bundle_support)
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))
compatibility_report = build_plugin_compatibility_report(root / "skills")
compatibility = compatibility_by_skill_id(compatibility_report)
bundles = load_editorial_bundles(root)
expected_doc = render_bundles_doc(root, metadata, bundles, compatibility)
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())