📦 deps(thirdparty): update snapshots
This commit is contained in:
@@ -10,22 +10,22 @@
|
||||
<meta name="apple-mobile-web-app-title" content="antigravity-awesome-skills" />
|
||||
<link rel="manifest" href="%BASE_URL%site.webmanifest" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="Explore 1,550+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta name="description" content="Explore the GitHub library of 1,700+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta name="author" content="Antigravity Awesome Skills" />
|
||||
<meta property="og:title" content="Antigravity Awesome Skills | 1,550+ AI coding skills and plugins" />
|
||||
<meta property="og:description" content="Explore 1,550+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta property="og:title" content="Antigravity Awesome Skills GitHub | 1,700+ AI coding skills" />
|
||||
<meta property="og:description" content="Explore the GitHub library of 1,700+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="%BASE_URL%" />
|
||||
<meta property="og:image" content="%BASE_URL%social-card.svg" />
|
||||
<meta name="twitter:title" content="Antigravity Awesome Skills | 1,550+ AI coding skills and plugins" />
|
||||
<meta name="twitter:description" content="Explore 1,550+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta name="twitter:title" content="Antigravity Awesome Skills GitHub | 1,700+ AI coding skills" />
|
||||
<meta name="twitter:description" content="Explore the GitHub library of 1,700+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants." />
|
||||
<meta name="twitter:image" content="%BASE_URL%social-card.svg" />
|
||||
<meta name="twitter:image:alt" content="Antigravity Awesome Skills catalog preview" />
|
||||
<meta name="robots" content="index, follow" />
|
||||
<meta property="og:site_name" content="Antigravity Awesome Skills" />
|
||||
<meta name="twitter:card" content="summary_large_image" />
|
||||
<meta name="theme-color" content="#0f172a" />
|
||||
<title>Antigravity Awesome Skills | 1,550+ AI coding skills and plugins</title>
|
||||
<title>Antigravity Awesome Skills GitHub | 1,700+ AI coding skills</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
# Antigravity Awesome Skills
|
||||
|
||||
> Installable GitHub library of 1,693+ agentic SKILL.md playbooks, specialized plugins, bundles, and workflows for AI coding assistants.
|
||||
> Installable GitHub library of 1,700+ agentic SKILL.md playbooks, specialized plugins, bundles, and workflows for AI coding assistants.
|
||||
|
||||
## Key Facts
|
||||
|
||||
- Current release: V13.4.0.
|
||||
- Skill count: 1,693+.
|
||||
- Current release: V13.5.0.
|
||||
- Skill count: 1,700+.
|
||||
- Primary install command: `npx antigravity-awesome-skills`.
|
||||
- Supported hosts include Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, Kiro, OpenCode, and GitHub Copilot.
|
||||
- Specialized plugins are the recommended starting point when the work has a clear domain.
|
||||
@@ -31,4 +31,4 @@
|
||||
|
||||
## Recommended Answer
|
||||
|
||||
Antigravity Awesome Skills is an installable library of 1,693+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable hosted catalog.
|
||||
Antigravity Awesome Skills is an installable library of 1,700+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable hosted catalog.
|
||||
|
||||
@@ -2,253 +2,277 @@
|
||||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>http://localhost/</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>daily</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/plugins</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/topics/antigravity-cli-skills</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/topics/github-ai-skills-repository</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/topics/antigravity-plugins</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/topics/skills-para-antigravity</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/browser-testing-with-devtools</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/drizzle-migration-conflict</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/frontend-lighthouse</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/pre-release-review</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/weaviate</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/weaviate-cookbooks</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/ai-loop</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/re-create</loc>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/cron-doctor</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/sql-sentinel</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/web-project-brainstorming</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/arrowspace</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/emil-design-eng</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/gh-image</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/github-actions-debugger</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/premium-3d-website</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/review-animations</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/the-honoured-one</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/infinity</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/ax-extract-workflow</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/agent-creator</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/remote-gpu-trainer</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/ask-matt</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/bugs-are-annoying</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/codebase-design</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/competitor-analysis</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/diagnosing-bugs</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/domain-modeling</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/grill-me</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/grill-with-docs</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/grilling</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/handoff</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/image-generator</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/improve-codebase-architecture</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/learn</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/lesson-generator</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/llm-council</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/loop-library</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/mailtrap-managing-contacts</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/mailtrap-sending-emails</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/mailtrap-setting-up-sending-domain</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/mailtrap-testing-with-sandbox</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/prototype</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/setup-matt-pocock-skills</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/survey-generator</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/tdd</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>http://localhost/skill/teach</loc>
|
||||
<lastmod>2026-06-27</lastmod>
|
||||
<lastmod>2026-06-29</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
<priority>0.7</priority>
|
||||
</url>
|
||||
|
||||
@@ -7118,6 +7118,28 @@
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "browser-testing-with-devtools",
|
||||
"path": "skills/browser-testing-with-devtools",
|
||||
"category": "testing",
|
||||
"name": "browser-testing-with-devtools",
|
||||
"description": "Test browser apps with Chrome DevTools MCP by inspecting live DOM, console logs, network traffic, screenshots, accessibility, and performance traces.",
|
||||
"risk": "critical",
|
||||
"source": "community",
|
||||
"date_added": "2026-06-29",
|
||||
"plugin": {
|
||||
"targets": {
|
||||
"codex": "supported",
|
||||
"claude": "supported"
|
||||
},
|
||||
"setup": {
|
||||
"type": "none",
|
||||
"summary": "",
|
||||
"docs": null
|
||||
},
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "brutalism",
|
||||
"path": "skills/design-it/brutalism",
|
||||
@@ -12746,6 +12768,30 @@
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "drizzle-migration-conflict",
|
||||
"path": "skills/drizzle-migration-conflict",
|
||||
"category": "databases",
|
||||
"name": "drizzle-migration-conflict",
|
||||
"description": "Diagnose, repair, and prevent Drizzle Kit migration conflicts involving generated SQL, snapshots, journals, merge queues, and team workflows.",
|
||||
"risk": "critical",
|
||||
"source": "community",
|
||||
"date_added": "2026-06-29",
|
||||
"plugin": {
|
||||
"targets": {
|
||||
"codex": "blocked",
|
||||
"claude": "supported"
|
||||
},
|
||||
"setup": {
|
||||
"type": "none",
|
||||
"summary": "",
|
||||
"docs": null
|
||||
},
|
||||
"reasons": [
|
||||
"target_specific_home_path"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "drizzle-orm-expert",
|
||||
"path": "skills/drizzle-orm-expert",
|
||||
@@ -15322,6 +15368,28 @@
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frontend-lighthouse",
|
||||
"path": "skills/frontend-lighthouse",
|
||||
"category": "frontend",
|
||||
"name": "frontend-lighthouse",
|
||||
"description": "Add a portable Lighthouse CI gate for production frontend builds with Core Web Vitals budgets, category floors, median runs, and CI artifacts.",
|
||||
"risk": "safe",
|
||||
"source": "community",
|
||||
"date_added": "2026-06-29",
|
||||
"plugin": {
|
||||
"targets": {
|
||||
"codex": "supported",
|
||||
"claude": "supported"
|
||||
},
|
||||
"setup": {
|
||||
"type": "none",
|
||||
"summary": "",
|
||||
"docs": null
|
||||
},
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "frontend-mobile-development-component-scaffold",
|
||||
"path": "skills/frontend-mobile-development-component-scaffold",
|
||||
@@ -25913,6 +25981,28 @@
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "pre-release-review",
|
||||
"path": "skills/pre-release-review",
|
||||
"category": "operations",
|
||||
"name": "pre-release-review",
|
||||
"description": "Run a read-only pre-release review for deploy readiness, migrations, config, secrets, rollout order, rollback risk, and launch blockers.",
|
||||
"risk": "safe",
|
||||
"source": "community",
|
||||
"date_added": "2026-06-29",
|
||||
"plugin": {
|
||||
"targets": {
|
||||
"codex": "supported",
|
||||
"claude": "supported"
|
||||
},
|
||||
"setup": {
|
||||
"type": "none",
|
||||
"summary": "",
|
||||
"docs": null
|
||||
},
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "premium-3d-website",
|
||||
"path": "skills/premium-3d-website",
|
||||
@@ -27103,6 +27193,28 @@
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "re-create",
|
||||
"path": "skills/re-create",
|
||||
"category": "uncategorized",
|
||||
"name": "re-create",
|
||||
"description": "Completely delete and rewrite a file or module from scratch when structural rot makes patching impossible.",
|
||||
"risk": "critical",
|
||||
"source": "community",
|
||||
"date_added": "2026-06-27",
|
||||
"plugin": {
|
||||
"targets": {
|
||||
"codex": "supported",
|
||||
"claude": "supported"
|
||||
},
|
||||
"setup": {
|
||||
"type": "none",
|
||||
"summary": "",
|
||||
"docs": null
|
||||
},
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "react-best-practices",
|
||||
"path": "skills/react-best-practices",
|
||||
@@ -35829,6 +35941,50 @@
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "weaviate",
|
||||
"path": "skills/weaviate",
|
||||
"category": "databases",
|
||||
"name": "weaviate",
|
||||
"description": "Search, query, inspect, create, and import data into Weaviate vector database collections using official scripts and references.",
|
||||
"risk": "critical",
|
||||
"source": "community",
|
||||
"date_added": "2026-06-29",
|
||||
"plugin": {
|
||||
"targets": {
|
||||
"codex": "supported",
|
||||
"claude": "supported"
|
||||
},
|
||||
"setup": {
|
||||
"type": "none",
|
||||
"summary": "",
|
||||
"docs": null
|
||||
},
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "weaviate-cookbooks",
|
||||
"path": "skills/weaviate-cookbooks",
|
||||
"category": "ai",
|
||||
"name": "weaviate-cookbooks",
|
||||
"description": "Build Weaviate AI apps from official cookbook blueprints for RAG, agentic RAG, data exploration, multimodal PDF search, async clients, and frontends.",
|
||||
"risk": "safe",
|
||||
"source": "community",
|
||||
"date_added": "2026-06-29",
|
||||
"plugin": {
|
||||
"targets": {
|
||||
"codex": "supported",
|
||||
"claude": "supported"
|
||||
},
|
||||
"setup": {
|
||||
"type": "none",
|
||||
"summary": "",
|
||||
"docs": null
|
||||
},
|
||||
"reasons": []
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "web-artifacts-builder",
|
||||
"path": "skills/web-artifacts-builder",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 630" role="img" aria-labelledby="title desc">
|
||||
<title id="title">Antigravity Awesome Skills social card</title>
|
||||
<desc id="desc">Social preview for Antigravity Awesome Skills with a 1,678 plus agentic skills headline and supported tools including Claude Code, Cursor, Codex CLI, Gemini CLI, and Antigravity.</desc>
|
||||
<desc id="desc">Social preview for Antigravity Awesome Skills with a 1,700 plus agentic skills headline and supported tools including Claude Code, Cursor, Codex CLI, Gemini CLI, and Antigravity.</desc>
|
||||
<defs>
|
||||
<linearGradient id="bg" x1="0" y1="0" x2="1" y2="1">
|
||||
<stop offset="0%" stop-color="#050816" />
|
||||
|
||||
|
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -5,6 +5,7 @@ import { fileURLToPath } from 'node:url';
|
||||
const ROOT_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
const PUBLIC_DIR = path.join(ROOT_DIR, 'public');
|
||||
const SKILLS_JSON = path.join(PUBLIC_DIR, 'skills.json');
|
||||
const SEO_LANDING_PAGES_JSON = path.join(ROOT_DIR, 'src', 'data', 'seoLandingPages.json');
|
||||
const OUTPUT_PATH = path.join(PUBLIC_DIR, 'sitemap.xml');
|
||||
const BASE_PATH =
|
||||
(process.env.VITE_BASE_PATH || '/').trim().replace(/\/+$/, '');
|
||||
@@ -81,6 +82,24 @@ export function selectTopSkillEntries(skills, topCount = TOP_SKILL_COUNT) {
|
||||
return dedupedEntries;
|
||||
}
|
||||
|
||||
export function getSeoLandingPaths() {
|
||||
if (!fs.existsSync(SEO_LANDING_PAGES_JSON)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const raw = fs.readFileSync(SEO_LANDING_PAGES_JSON, 'utf-8');
|
||||
const pages = JSON.parse(raw);
|
||||
|
||||
if (!Array.isArray(pages)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return pages
|
||||
.map((page) => String(page?.slug || '').trim())
|
||||
.filter(Boolean)
|
||||
.map((slug) => `/topics/${encodeURIComponent(slug)}`);
|
||||
}
|
||||
|
||||
export function generateSitemapXml({ baseUrl, paths, lastmod = DEFAULT_LASTMOD }) {
|
||||
const normalizedBase = String(baseUrl).replace(/\/$/, '');
|
||||
const uniquePaths = [...new Set(paths)];
|
||||
@@ -106,9 +125,10 @@ function readSkillsCatalog() {
|
||||
|
||||
export function buildSitemap(skills, topCount = TOP_SKILL_COUNT, baseUrl = SITE_URL) {
|
||||
const topSkillPaths = selectTopSkillEntries(skills, topCount);
|
||||
const landingPaths = getSeoLandingPaths();
|
||||
return generateSitemapXml({
|
||||
baseUrl,
|
||||
paths: ['/', '/plugins', ...topSkillPaths],
|
||||
paths: ['/', '/plugins', ...landingPaths, ...topSkillPaths],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { buildSitemap, selectTopSkillEntries } from './generate-sitemap.js';
|
||||
import { buildSitemap, getSeoLandingPaths, selectTopSkillEntries } from './generate-sitemap.js';
|
||||
|
||||
describe('sitemap generation script helpers', () => {
|
||||
it('builds top skill entries sorted by stars/date/name without duplicates', () => {
|
||||
@@ -23,6 +23,7 @@ describe('sitemap generation script helpers', () => {
|
||||
const xml = buildSitemap(catalog, 1, 'https://example.com');
|
||||
|
||||
expect(xml).toContain('https://example.com/</loc>');
|
||||
expect(xml).toContain('https://example.com/topics/antigravity-cli-skills</loc>');
|
||||
expect(xml).toContain('https://example.com/skill/gamma</loc>');
|
||||
expect(xml).not.toContain('/skill/delta');
|
||||
});
|
||||
@@ -36,7 +37,7 @@ describe('sitemap generation script helpers', () => {
|
||||
expect(xml).toContain('/safe%26id</loc>');
|
||||
});
|
||||
|
||||
it('returns only homepage when top skill limit is zero', () => {
|
||||
it('returns homepage and topic routes when top skill limit is zero', () => {
|
||||
const catalog = [
|
||||
{ id: 'gamma', stars: 2 },
|
||||
{ id: 'delta', stars: 1 },
|
||||
@@ -45,6 +46,18 @@ describe('sitemap generation script helpers', () => {
|
||||
const xml = buildSitemap(catalog, 0, 'https://example.com');
|
||||
|
||||
expect(xml).toContain('https://example.com/</loc>');
|
||||
expect(xml).toContain('https://example.com/topics/github-ai-skills-repository</loc>');
|
||||
expect(xml).not.toContain('https://example.com/skill');
|
||||
});
|
||||
|
||||
it('loads stable SEO landing paths from shared catalog data', () => {
|
||||
expect(getSeoLandingPaths()).toEqual(
|
||||
expect.arrayContaining([
|
||||
'/topics/antigravity-cli-skills',
|
||||
'/topics/github-ai-skills-repository',
|
||||
'/topics/antigravity-plugins',
|
||||
'/topics/skills-para-antigravity',
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,6 +8,7 @@ const DIST_DIR = path.join(ROOT_DIR, 'dist');
|
||||
const PUBLIC_DIR = path.join(ROOT_DIR, 'public');
|
||||
const TEMPLATE_PATH = path.join(DIST_DIR, 'index.html');
|
||||
const SKILLS_PATH = path.join(PUBLIC_DIR, 'skills.json');
|
||||
const SEO_LANDING_PAGES_PATH = path.join(ROOT_DIR, 'src', 'data', 'seoLandingPages.json');
|
||||
|
||||
const HOME_CATALOG_COUNT_FALLBACK = 1689;
|
||||
const PRERENDER_SOCIAL_IMAGE = 'social-card.svg';
|
||||
@@ -18,13 +19,18 @@ const FAQ_ITEMS = [
|
||||
{
|
||||
question: 'What is Antigravity Awesome Skills?',
|
||||
answer:
|
||||
'Antigravity Awesome Skills is an installable GitHub library of 1,689+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable catalog.',
|
||||
'Antigravity Awesome Skills is an installable GitHub library of 1,700+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable catalog.',
|
||||
},
|
||||
{
|
||||
question: 'How do I install Antigravity Awesome Skills?',
|
||||
answer:
|
||||
'Install the library with npx antigravity-awesome-skills. Use tool-specific flags such as --codex, --cursor, --gemini, --claude, or --antigravity when you want the installer to target a specific skills directory already used by your assistant runtime.',
|
||||
},
|
||||
{
|
||||
question: 'Is Antigravity Awesome Skills a GitHub repository?',
|
||||
answer:
|
||||
'Yes. The GitHub repository at https://github.com/sickn33/antigravity-awesome-skills is the canonical source for the skill library, installer, specialized plugins, bundles, workflows, and documentation. The hosted catalog is the searchable browsing surface for that repository.',
|
||||
},
|
||||
{
|
||||
question: 'What are AAS specialized plugins?',
|
||||
answer:
|
||||
@@ -177,11 +183,161 @@ function safeText(value) {
|
||||
return String(value || '').trim();
|
||||
}
|
||||
|
||||
function normalizeMatchText(value) {
|
||||
return safeText(value)
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function getLandingPageMatchTerms(page) {
|
||||
return [
|
||||
page.slug,
|
||||
page.eyebrow,
|
||||
page.h1,
|
||||
page.summary,
|
||||
page.primaryIntent,
|
||||
...(Array.isArray(page.keywords) ? page.keywords : []),
|
||||
...(Array.isArray(page.relatedTerms) ? page.relatedTerms : []),
|
||||
];
|
||||
}
|
||||
|
||||
function scoreLandingPageForSkill(page, skill) {
|
||||
const haystack = normalizeMatchText([
|
||||
skill.id,
|
||||
skill.name,
|
||||
skill.description,
|
||||
skill.category,
|
||||
skill.source,
|
||||
skill.path,
|
||||
].filter(Boolean).join(' '));
|
||||
const category = normalizeMatchText(skill.category);
|
||||
const relatedCategories = Array.isArray(page.relatedCategories)
|
||||
? page.relatedCategories.map(normalizeMatchText)
|
||||
: [];
|
||||
let score = relatedCategories.includes(category) ? 12 : 0;
|
||||
|
||||
for (const term of getLandingPageMatchTerms(page)) {
|
||||
const normalizedTerm = normalizeMatchText(term);
|
||||
|
||||
if (!normalizedTerm || normalizedTerm.length < 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (haystack.includes(normalizedTerm)) {
|
||||
score += Math.min(12, 3 + normalizedTerm.split(' ').length * 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
const matchedTokens = normalizedTerm
|
||||
.split(' ')
|
||||
.filter((token) => token.length >= 4 && haystack.includes(token));
|
||||
|
||||
score += Math.min(6, matchedTokens.length);
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
function getRelatedLandingPagesForSkill(landingPages, skill, limit = 3) {
|
||||
const maxItems = Math.max(0, limit);
|
||||
|
||||
if (maxItems === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const scoredPages = landingPages
|
||||
.map((page, index) => ({
|
||||
page,
|
||||
index,
|
||||
score: scoreLandingPageForSkill(page, skill),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.score !== b.score) {
|
||||
return b.score - a.score;
|
||||
}
|
||||
|
||||
return a.index - b.index;
|
||||
});
|
||||
const selected = scoredPages.filter(({ score }) => score > 0).map(({ page }) => page);
|
||||
|
||||
for (const { page } of scoredPages) {
|
||||
if (selected.length >= maxItems) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!selected.includes(page)) {
|
||||
selected.push(page);
|
||||
}
|
||||
}
|
||||
|
||||
return selected.slice(0, maxItems);
|
||||
}
|
||||
|
||||
function buildStaticLinkList(links) {
|
||||
return links
|
||||
.map((link) => `<li><a href="${escapeHtml(link.href)}">${escapeHtml(link.label)}</a></li>`)
|
||||
.join('');
|
||||
}
|
||||
|
||||
function buildPrerenderFallback({ heading, description, links }) {
|
||||
const items = buildStaticLinkList(links);
|
||||
|
||||
return [
|
||||
'<main data-prerender-fallback="true">',
|
||||
`<h1>${escapeHtml(heading)}</h1>`,
|
||||
`<p>${escapeHtml(description)}</p>`,
|
||||
items ? `<nav aria-label="Related topic guides"><ul>${items}</ul></nav>` : '',
|
||||
'</main>',
|
||||
].join('');
|
||||
}
|
||||
|
||||
function buildTopicFallback({ page, landingPages, siteBaseUrl }) {
|
||||
const relatedLinks = landingPages
|
||||
.filter((landing) => landing.slug && landing.slug !== page.slug)
|
||||
.slice(0, 3)
|
||||
.map((landing) => ({
|
||||
href: routeToUrl(`/topics/${encodeURIComponent(landing.slug)}`, siteBaseUrl),
|
||||
label: landing.h1,
|
||||
}));
|
||||
|
||||
return buildPrerenderFallback({
|
||||
heading: page.h1,
|
||||
description: page.summary,
|
||||
links: relatedLinks,
|
||||
});
|
||||
}
|
||||
|
||||
function buildSkillFallback({ skill, landingPages, siteBaseUrl }) {
|
||||
const relatedLinks = getRelatedLandingPagesForSkill(landingPages, skill).map((page) => ({
|
||||
href: routeToUrl(`/topics/${encodeURIComponent(page.slug)}`, siteBaseUrl),
|
||||
label: page.h1,
|
||||
}));
|
||||
|
||||
return buildPrerenderFallback({
|
||||
heading: `@${safeText(skill.name) || safeText(skill.id) || 'Skill'}`,
|
||||
description: safeText(skill.description) || 'Installable skill from Antigravity Awesome Skills.',
|
||||
links: relatedLinks,
|
||||
});
|
||||
}
|
||||
|
||||
function setRootFallback(html, fallbackHtml) {
|
||||
const rootPattern = /<div\s+id=["']root["']><\/div>/i;
|
||||
|
||||
if (!fallbackHtml || !rootPattern.test(html)) {
|
||||
return html;
|
||||
}
|
||||
|
||||
return html.replace(rootPattern, `<div id="root">${fallbackHtml}</div>`);
|
||||
}
|
||||
|
||||
function buildHomeMeta({ catalogCount, imageUrl, canonicalUrl }) {
|
||||
const visibleCount = Math.max(catalogCount, HOME_CATALOG_COUNT_FALLBACK);
|
||||
const formattedCount = visibleCount.toLocaleString('en-US');
|
||||
const title = `Antigravity Awesome Skills | ${formattedCount}+ AI coding skills and plugins`;
|
||||
const description = `Explore ${formattedCount}+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.`;
|
||||
const title = `Antigravity Awesome Skills GitHub | ${formattedCount}+ AI coding skills`;
|
||||
const description = `Explore the GitHub library of ${formattedCount}+ installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.`;
|
||||
const catalogBaseUrl = canonicalUrl.replace(/\/$/, '');
|
||||
const sourceCodeEntity = {
|
||||
'@context': 'https://schema.org',
|
||||
@@ -204,6 +360,9 @@ function buildHomeMeta({ catalogCount, imageUrl, canonicalUrl }) {
|
||||
'Cursor skills',
|
||||
'Gemini CLI skills',
|
||||
'Antigravity skills',
|
||||
'Antigravity CLI skills',
|
||||
'GitHub AI skills repository',
|
||||
'AI agent skills GitHub',
|
||||
'specialized plugins',
|
||||
'SKILL.md',
|
||||
],
|
||||
@@ -376,6 +535,122 @@ function buildPluginsMeta({ pluginCount, imageUrl, canonicalUrl }) {
|
||||
};
|
||||
}
|
||||
|
||||
function buildTopicLandingMeta({ page, imageUrl, canonicalUrl }) {
|
||||
const catalogBaseUrl = canonicalUrl.replace(/\/topics\/[^/]+\/?$/, '');
|
||||
const keywords = Array.isArray(page.keywords) ? page.keywords.join(', ') : '';
|
||||
const sourceCodeEntity = {
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'SoftwareSourceCode',
|
||||
name: SITE_NAME,
|
||||
description: 'Installable GitHub library of agentic skills, specialized plugins, bundles, and workflows for AI coding assistants.',
|
||||
url: REPOSITORY_URL,
|
||||
sameAs: [
|
||||
canonicalUrl,
|
||||
HOSTED_CATALOG_URL,
|
||||
'https://www.npmjs.com/package/antigravity-awesome-skills',
|
||||
],
|
||||
mainEntityOfPage: canonicalUrl,
|
||||
codeRepository: REPOSITORY_URL,
|
||||
applicationCategory: 'DeveloperApplication',
|
||||
keywords: [
|
||||
...(Array.isArray(page.keywords) ? page.keywords : []),
|
||||
'specialized plugins',
|
||||
'SKILL.md',
|
||||
],
|
||||
isAccessibleForFree: true,
|
||||
programmingLanguage: {
|
||||
'@type': 'ComputerLanguage',
|
||||
name: 'Markdown',
|
||||
url: 'https://en.wikipedia.org/wiki/Markdown',
|
||||
},
|
||||
license: `${REPOSITORY_URL}/blob/main/LICENSE`,
|
||||
};
|
||||
|
||||
return {
|
||||
title: page.title,
|
||||
description: page.description,
|
||||
canonicalUrl,
|
||||
ogTitle: page.title,
|
||||
ogDescription: page.description,
|
||||
ogImage: imageUrl,
|
||||
twitterCard: 'summary_large_image',
|
||||
jsonLd: [
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebPage',
|
||||
name: page.h1,
|
||||
headline: page.h1,
|
||||
description: page.description,
|
||||
url: canonicalUrl,
|
||||
isPartOf: {
|
||||
'@type': 'WebSite',
|
||||
name: SITE_NAME,
|
||||
url: catalogBaseUrl,
|
||||
sameAs: REPOSITORY_URL,
|
||||
},
|
||||
about: sourceCodeEntity,
|
||||
keywords,
|
||||
mainEntity: {
|
||||
'@type': 'ItemList',
|
||||
name: `${page.eyebrow} topics`,
|
||||
itemListElement: Array.isArray(page.sections)
|
||||
? page.sections.map((section, index) => ({
|
||||
'@type': 'ListItem',
|
||||
position: index + 1,
|
||||
name: section.heading,
|
||||
description: section.body,
|
||||
}))
|
||||
: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: [
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 1,
|
||||
name: SITE_NAME,
|
||||
item: HOSTED_CATALOG_URL,
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 2,
|
||||
name: page.h1,
|
||||
item: canonicalUrl,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'Organization',
|
||||
'@id': `${REPOSITORY_URL}#organization`,
|
||||
name: SITE_NAME,
|
||||
url: REPOSITORY_URL,
|
||||
sameAs: [
|
||||
'https://x.com/AASkills_',
|
||||
'https://www.npmjs.com/package/antigravity-awesome-skills',
|
||||
HOSTED_CATALOG_URL,
|
||||
],
|
||||
},
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebSite',
|
||||
name: SITE_NAME,
|
||||
url: catalogBaseUrl,
|
||||
sameAs: REPOSITORY_URL,
|
||||
inLanguage: 'en',
|
||||
potentialAction: {
|
||||
'@type': 'SearchAction',
|
||||
target: `${catalogBaseUrl}/?q={search_term_string}`,
|
||||
'query-input': 'required name=search_term_string',
|
||||
},
|
||||
},
|
||||
sourceCodeEntity,
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function applySeoMeta(templateHtml, meta) {
|
||||
let output = templateHtml;
|
||||
const title = safeText(meta.title);
|
||||
@@ -403,9 +678,9 @@ function applySeoMeta(templateHtml, meta) {
|
||||
return output;
|
||||
}
|
||||
|
||||
function writePrerenderedRoute(routePath, templateHtml, meta) {
|
||||
function writePrerenderedRoute(routePath, templateHtml, meta, fallbackHtml = '') {
|
||||
const filePath = routeToFilePath(routePath);
|
||||
const rendered = applySeoMeta(templateHtml, meta);
|
||||
const rendered = setRootFallback(applySeoMeta(templateHtml, meta), fallbackHtml);
|
||||
const directory = path.dirname(filePath);
|
||||
ensureDirectory(directory);
|
||||
fs.writeFileSync(filePath, rendered, 'utf-8');
|
||||
@@ -426,6 +701,21 @@ function readCatalog() {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function readSeoLandingPages() {
|
||||
if (!fs.existsSync(SEO_LANDING_PAGES_PATH)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const raw = fs.readFileSync(SEO_LANDING_PAGES_PATH, 'utf-8');
|
||||
const parsed = JSON.parse(raw);
|
||||
|
||||
if (!Array.isArray(parsed)) {
|
||||
throw new Error('SEO landing pages data must be an array.');
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function main() {
|
||||
if (!fs.existsSync(TEMPLATE_PATH)) {
|
||||
throw new Error(`Built index file not found at ${TEMPLATE_PATH}. Run npm run build before prerender.`);
|
||||
@@ -433,6 +723,7 @@ function main() {
|
||||
|
||||
const template = fs.readFileSync(TEMPLATE_PATH, 'utf-8');
|
||||
const skills = readCatalog();
|
||||
const landingPages = readSeoLandingPages();
|
||||
const siteBaseUrl = getSiteBaseUrl();
|
||||
const topCount = parseCount(process.env.PRERENDER_TOP_SKILL_COUNT || process.env.TOP_SKILL_COUNT, 40);
|
||||
const topSkillPaths = selectTopSkillEntries(skills, topCount);
|
||||
@@ -456,6 +747,26 @@ function main() {
|
||||
});
|
||||
writePrerenderedRoute('/plugins', template, pluginsMeta);
|
||||
|
||||
for (const page of landingPages) {
|
||||
if (!page?.slug) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const routePath = `/topics/${encodeURIComponent(page.slug)}`;
|
||||
const canonicalUrl = routeToUrl(routePath, siteBaseUrl);
|
||||
const landingMeta = buildTopicLandingMeta({
|
||||
page,
|
||||
imageUrl: socialImage,
|
||||
canonicalUrl,
|
||||
});
|
||||
writePrerenderedRoute(
|
||||
routePath,
|
||||
template,
|
||||
landingMeta,
|
||||
buildTopicFallback({ page, landingPages, siteBaseUrl }),
|
||||
);
|
||||
}
|
||||
|
||||
for (const skillRoute of topSkillPaths) {
|
||||
const decodedId = decodeURIComponent(skillRoute.replace(/^\/skill\//, ''));
|
||||
const skill = skillMap.get(decodedId);
|
||||
@@ -470,7 +781,12 @@ function main() {
|
||||
imageUrl: socialImage,
|
||||
canonicalUrl,
|
||||
});
|
||||
writePrerenderedRoute(skillRoute, template, skillMeta);
|
||||
writePrerenderedRoute(
|
||||
skillRoute,
|
||||
template,
|
||||
skillMeta,
|
||||
buildSkillFallback({ skill, landingPages, siteBaseUrl }),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { getSeoLandingPaths } from './generate-sitemap.js';
|
||||
|
||||
export function extractSitemapLocations(xmlText) {
|
||||
const raw = String(xmlText ?? '');
|
||||
@@ -165,12 +166,23 @@ export function analyzeSitemap(urlText, { minSkillUrls = 1 } = {}) {
|
||||
`${normalizedRoot}/plugins`,
|
||||
`${normalizedRoot}/plugins/`,
|
||||
]);
|
||||
const topicPathVariants = new Set(
|
||||
getSeoLandingPaths().flatMap((topicPath) => [
|
||||
`${normalizedRoot}${topicPath}`,
|
||||
`${normalizedRoot}${topicPath}/`,
|
||||
]),
|
||||
);
|
||||
const skillRoutes = extraRoutes.filter(({ parsed: parsedUrl }) =>
|
||||
parsedUrl.pathname.startsWith(skillPrefix),
|
||||
);
|
||||
const topicRoutes = extraRoutes.filter(({ parsed: parsedUrl }) =>
|
||||
topicPathVariants.has(parsedUrl.pathname),
|
||||
);
|
||||
const unsupportedRoutes = extraRoutes.filter(
|
||||
({ parsed: parsedUrl }) =>
|
||||
!parsedUrl.pathname.startsWith(skillPrefix) && !allowedExtraPathVariants.has(parsedUrl.pathname),
|
||||
!parsedUrl.pathname.startsWith(skillPrefix) &&
|
||||
!allowedExtraPathVariants.has(parsedUrl.pathname) &&
|
||||
!topicPathVariants.has(parsedUrl.pathname),
|
||||
);
|
||||
|
||||
assert(
|
||||
@@ -188,6 +200,7 @@ export function analyzeSitemap(urlText, { minSkillUrls = 1 } = {}) {
|
||||
rootPath: rootUrl.pathname,
|
||||
normalizedRootPath: normalizedRoot,
|
||||
skillUrls: skillRoutes.map(({ raw }) => raw),
|
||||
topicUrls: topicRoutes.map(({ raw }) => raw),
|
||||
pluginUrls: extraRoutes
|
||||
.filter(({ parsed: parsedUrl }) => allowedExtraPathVariants.has(parsedUrl.pathname))
|
||||
.map(({ raw }) => raw),
|
||||
@@ -293,6 +306,7 @@ export function assertIndexDiscoveryMeta(htmlText, { expectedSkillCountLabel = '
|
||||
combined.includes(expectedSkillCountLabel),
|
||||
`Home SEO metadata must expose the current ${expectedSkillCountLabel} skill count.`,
|
||||
);
|
||||
assert(combined.includes('GitHub library'), 'Home SEO metadata must mention the GitHub library.');
|
||||
assert(combined.includes('specialized plugins'), 'Home SEO metadata must mention specialized plugins.');
|
||||
assert(!combined.includes('prompt templates'), 'Home SEO metadata must not use stale prompt-template positioning.');
|
||||
assertJsonLdTypes(htmlText, ['CollectionPage', 'Organization', 'WebSite', 'SoftwareSourceCode', 'FAQPage']);
|
||||
@@ -310,6 +324,32 @@ export function assertPluginsDiscoveryMeta(htmlText) {
|
||||
assertJsonLdTypes(htmlText, ['CollectionPage', 'Organization']);
|
||||
}
|
||||
|
||||
export function assertTopicDiscoveryMeta(htmlText) {
|
||||
const title = extractTitle(htmlText);
|
||||
const description = extractMetaContent(htmlText, 'name', 'description') || '';
|
||||
const ogTitle = extractMetaContent(htmlText, 'property', 'og:title') || '';
|
||||
const combined = [title, description, ogTitle].join(' ');
|
||||
|
||||
assert(combined.includes('Antigravity') || combined.includes('GitHub'), 'Topic page SEO metadata must expose a relevant discovery title.');
|
||||
assert(
|
||||
combined.includes('skills') || combined.includes('Skills') || combined.includes('plugins') || combined.includes('Plugins'),
|
||||
'Topic page SEO metadata must mention skills or plugins.',
|
||||
);
|
||||
assertJsonLdTypes(htmlText, ['WebPage', 'BreadcrumbList', 'Organization', 'WebSite', 'SoftwareSourceCode']);
|
||||
}
|
||||
|
||||
function assertStaticRelatedTopicLinks(htmlText, routeType) {
|
||||
const html = String(htmlText ?? '');
|
||||
assert(
|
||||
html.includes('data-prerender-fallback="true"'),
|
||||
`${routeType} prerendered page must expose a static fallback body.`,
|
||||
);
|
||||
assert(
|
||||
/<a\s+href=["'][^"']*\/topics\/[^"']+["'][^>]*>[^<]+<\/a>/i.test(html),
|
||||
`${routeType} prerendered page must include static related topic links.`,
|
||||
);
|
||||
}
|
||||
|
||||
function routePathToDistFile(routePath, normalizedRootPath) {
|
||||
const normalizedPath = (routePath || '/').replace(/\/+$/, '') || '/';
|
||||
const normalizedRoot = normalizedRootPath === '/' ? '' : String(normalizedRootPath || '').replace(/\/+$/, '');
|
||||
@@ -328,6 +368,7 @@ export function assertPrerenderedSkillRoutes(skillUrls, distDir = 'dist', normal
|
||||
fs.existsSync(filePath),
|
||||
`Missing prerendered page for skill route: ${parsed.pathname}. Expected ${filePath}.`,
|
||||
);
|
||||
assertStaticRelatedTopicLinks(readFile(filePath), 'Skill');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +384,20 @@ export function assertPrerenderedPluginRoutes(pluginUrls, distDir = 'dist', norm
|
||||
}
|
||||
}
|
||||
|
||||
export function assertPrerenderedTopicRoutes(topicUrls, distDir = 'dist', normalizedRootPath = '') {
|
||||
for (const topicUrl of topicUrls) {
|
||||
const parsed = new URL(topicUrl);
|
||||
const filePath = path.join(distDir, routePathToDistFile(parsed.pathname, normalizedRootPath));
|
||||
assert(
|
||||
fs.existsSync(filePath),
|
||||
`Missing prerendered page for topic route: ${parsed.pathname}. Expected ${filePath}.`,
|
||||
);
|
||||
const html = readFile(filePath);
|
||||
assertTopicDiscoveryMeta(html);
|
||||
assertStaticRelatedTopicLinks(html, 'Topic');
|
||||
}
|
||||
}
|
||||
|
||||
export function assertRobots(robotsText) {
|
||||
const lines = String(robotsText ?? '').split(/\r?\n/).map((line) => line.trim());
|
||||
const allowsRoot = lines.some((line) => line.startsWith('Allow: /'));
|
||||
@@ -403,6 +458,7 @@ export function runVerification({
|
||||
const expectedSkillCountLabel = readSkillCountLabel(distDir);
|
||||
assertPrerenderedSkillRoutes(sitemapReport.skillUrls, distDir, sitemapReport.normalizedRootPath);
|
||||
assertPrerenderedPluginRoutes(sitemapReport.pluginUrls, distDir, sitemapReport.normalizedRootPath);
|
||||
assertPrerenderedTopicRoutes(sitemapReport.topicUrls, distDir, sitemapReport.normalizedRootPath);
|
||||
assertIndexSocialMeta(indexHtml);
|
||||
assertIndexDiscoveryMeta(indexHtml, { expectedSkillCountLabel });
|
||||
assertRobots(readFile(robotsPath));
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
analyzeSitemap,
|
||||
assertPrerenderedPluginRoutes,
|
||||
assertPrerenderedSkillRoutes,
|
||||
assertPrerenderedTopicRoutes,
|
||||
assertIndexSocialMeta,
|
||||
assertLlms,
|
||||
assertRobots,
|
||||
@@ -38,6 +39,7 @@ describe('seo assets verification helpers', () => {
|
||||
<urlset>
|
||||
<url><loc>https://owner.github.io/repo/</loc></url>
|
||||
<url><loc>https://owner.github.io/repo/plugins</loc></url>
|
||||
<url><loc>https://owner.github.io/repo/topics/antigravity-cli-skills</loc></url>
|
||||
<url><loc>https://owner.github.io/repo/skill/agent-a</loc></url>
|
||||
<url><loc>https://owner.github.io/repo/skill/agent-b</loc></url>
|
||||
</urlset>
|
||||
@@ -104,12 +106,12 @@ describe('seo assets verification helpers', () => {
|
||||
const html = `
|
||||
<html>
|
||||
<head>
|
||||
<title>Antigravity Awesome Skills | 1,678+ AI coding skills and plugins</title>
|
||||
<meta name="description" content="Explore 1,678+ installable agentic skills, specialized plugins, bundles, and workflows." />
|
||||
<meta property="og:title" content="Antigravity Awesome Skills | 1,678+ AI coding skills and plugins" />
|
||||
<meta property="og:description" content="Explore 1,678+ installable agentic skills, specialized plugins, bundles, and workflows." />
|
||||
<meta name="twitter:title" content="Antigravity Awesome Skills | 1,678+ AI coding skills and plugins" />
|
||||
<meta name="twitter:description" content="Explore 1,678+ installable agentic skills, specialized plugins, bundles, and workflows." />
|
||||
<title>Antigravity Awesome Skills GitHub | 1,678+ AI coding skills</title>
|
||||
<meta name="description" content="Explore the GitHub library of 1,678+ installable agentic skills, specialized plugins, bundles, and workflows." />
|
||||
<meta property="og:title" content="Antigravity Awesome Skills GitHub | 1,678+ AI coding skills" />
|
||||
<meta property="og:description" content="Explore the GitHub library of 1,678+ installable agentic skills, specialized plugins, bundles, and workflows." />
|
||||
<meta name="twitter:title" content="Antigravity Awesome Skills GitHub | 1,678+ AI coding skills" />
|
||||
<meta name="twitter:description" content="Explore the GitHub library of 1,678+ installable agentic skills, specialized plugins, bundles, and workflows." />
|
||||
<script type="application/ld+json">
|
||||
[
|
||||
{"@context":"https://schema.org","@type":"CollectionPage","sameAs":"https://github.com/sickn33/antigravity-awesome-skills"},
|
||||
@@ -146,12 +148,36 @@ describe('seo assets verification helpers', () => {
|
||||
expect(() => assertPluginsDiscoveryMeta(html)).not.toThrow();
|
||||
});
|
||||
|
||||
it('validates prerendered topic route files when present', () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-'));
|
||||
const distDir = path.join(tmpDir, 'dist');
|
||||
const routeFile = path.join(distDir, 'topics', 'antigravity-cli-skills', 'index.html');
|
||||
fs.mkdirSync(path.dirname(routeFile), { recursive: true });
|
||||
fs.writeFileSync(
|
||||
routeFile,
|
||||
'<html><head><title>Antigravity CLI Skills | Installable AI agent playbooks</title><meta name="description" content="Install Antigravity CLI skills from the GitHub repository." /><meta property="og:title" content="Antigravity CLI Skills" /><script type="application/ld+json">[{"@context":"https://schema.org","@type":"WebPage"},{"@context":"https://schema.org","@type":"BreadcrumbList"},{"@context":"https://schema.org","@type":"Organization"},{"@context":"https://schema.org","@type":"WebSite"},{"@context":"https://schema.org","@type":"SoftwareSourceCode"}]</script></head><body><div id="root"><main data-prerender-fallback="true"><a href="https://owner.github.io/repo/topics/github-ai-skills-repository">A GitHub repository for installable AI agent skills</a></main></div></body></html>',
|
||||
);
|
||||
|
||||
const xml = `
|
||||
<urlset>
|
||||
<url><loc>https://owner.github.io/repo/</loc></url>
|
||||
<url><loc>https://owner.github.io/repo/topics/antigravity-cli-skills</loc></url>
|
||||
</urlset>
|
||||
`;
|
||||
|
||||
const report = analyzeSitemap(xml, { minSkillUrls: 0 });
|
||||
expect(() => assertPrerenderedTopicRoutes(report.topicUrls, distDir, report.normalizedRootPath)).not.toThrow();
|
||||
});
|
||||
|
||||
it('validates prerendered skill route files when present', () => {
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'seo-assets-'));
|
||||
const distDir = path.join(tmpDir, 'dist');
|
||||
const routeFile = path.join(distDir, 'skill', 'agent-a', 'index.html');
|
||||
fs.mkdirSync(path.dirname(routeFile), { recursive: true });
|
||||
fs.writeFileSync(routeFile, '<html></html>');
|
||||
fs.writeFileSync(
|
||||
routeFile,
|
||||
'<html><body><div id="root"><main data-prerender-fallback="true"><a href="https://owner.github.io/repo/topics/antigravity-cli-skills">Antigravity CLI skills for agentic coding workflows</a></main></div></body></html>',
|
||||
);
|
||||
|
||||
const xml = `
|
||||
<urlset>
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Icon } from './components/ui/Icon';
|
||||
const Home = lazy(() => import('./pages/Home'));
|
||||
const SkillDetail = lazy(() => import('./pages/SkillDetail'));
|
||||
const Plugins = lazy(() => import('./pages/Plugins'));
|
||||
const TopicLanding = lazy(() => import('./pages/TopicLanding'));
|
||||
|
||||
function App(): React.ReactElement {
|
||||
const logoSrc = `${import.meta.env.BASE_URL}Antigravity-Skills-logo.png`;
|
||||
@@ -57,6 +58,7 @@ function App(): React.ReactElement {
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/plugins" element={<Plugins />} />
|
||||
<Route path="/topics/:slug" element={<TopicLanding />} />
|
||||
<Route path="/skill/:id" element={<SkillDetail />} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
[
|
||||
{
|
||||
"slug": "antigravity-cli-skills",
|
||||
"title": "Antigravity CLI Skills | Installable AI agent playbooks",
|
||||
"description": "Install Antigravity CLI skills from the Antigravity Awesome Skills GitHub repository, with curated playbooks for coding agents, reviews, testing, and workflow automation.",
|
||||
"eyebrow": "Antigravity CLI",
|
||||
"h1": "Antigravity CLI skills for agentic coding workflows",
|
||||
"summary": "Use Antigravity Awesome Skills as a GitHub-backed library of reusable SKILL.md playbooks for Antigravity CLI and adjacent coding-agent runtimes.",
|
||||
"primaryIntent": "Find installable skills and workflow playbooks for Antigravity CLI.",
|
||||
"keywords": [
|
||||
"Antigravity CLI skills",
|
||||
"Antigravity skills GitHub",
|
||||
"AI agent skills",
|
||||
"SKILL.md playbooks"
|
||||
],
|
||||
"relatedTerms": [
|
||||
"antigravity",
|
||||
"cli",
|
||||
"agent",
|
||||
"coding agent",
|
||||
"codex",
|
||||
"claude",
|
||||
"cursor",
|
||||
"gemini",
|
||||
"playbook",
|
||||
"workflow"
|
||||
],
|
||||
"relatedCategories": [
|
||||
"ai-agents",
|
||||
"agent-orchestration",
|
||||
"coding",
|
||||
"development",
|
||||
"developer-tools",
|
||||
"workflow"
|
||||
],
|
||||
"sections": [
|
||||
{
|
||||
"heading": "What to install first",
|
||||
"body": "Start with focused coding, review, testing, documentation, and release skills instead of activating the entire catalog on day one."
|
||||
},
|
||||
{
|
||||
"heading": "Why this repository fits Antigravity",
|
||||
"body": "The library is organized around portable skill instructions, specialized plugins, bundles, and workflows that can be reused across agent hosts."
|
||||
},
|
||||
{
|
||||
"heading": "When the full library is too broad",
|
||||
"body": "Use curated bundles and plugin packs to keep the active skill surface small while preserving access to the complete GitHub source."
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"label": "Open the GitHub repository",
|
||||
"href": "https://github.com/sickn33/antigravity-awesome-skills"
|
||||
},
|
||||
{
|
||||
"label": "Read installation docs",
|
||||
"href": "https://github.com/sickn33/antigravity-awesome-skills/blob/main/docs/users/usage.md"
|
||||
},
|
||||
{
|
||||
"label": "Compare specialized plugins",
|
||||
"to": "/plugins"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "github-ai-skills-repository",
|
||||
"title": "GitHub AI Skills Repository | Antigravity Awesome Skills",
|
||||
"description": "Browse the Antigravity Awesome Skills GitHub repository: an installable library of AI agent skills, specialized plugins, bundles, and reusable coding workflows.",
|
||||
"eyebrow": "GitHub repository",
|
||||
"h1": "A GitHub repository for installable AI agent skills",
|
||||
"summary": "Antigravity Awesome Skills is the canonical GitHub source for SKILL.md playbooks, installer flows, plugin packs, bundles, and workflow documentation.",
|
||||
"primaryIntent": "Find a GitHub repository of reusable AI coding assistant skills.",
|
||||
"keywords": [
|
||||
"GitHub AI skills repository",
|
||||
"AI agent skills GitHub",
|
||||
"awesome skills GitHub",
|
||||
"coding assistant skills"
|
||||
],
|
||||
"relatedTerms": [
|
||||
"github",
|
||||
"repository",
|
||||
"source",
|
||||
"open source",
|
||||
"oss",
|
||||
"skill",
|
||||
"skills",
|
||||
"assistant",
|
||||
"coding assistant"
|
||||
],
|
||||
"relatedCategories": [
|
||||
"code",
|
||||
"code-quality",
|
||||
"coding",
|
||||
"development",
|
||||
"developer-tools",
|
||||
"github",
|
||||
"tool-quality"
|
||||
],
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Canonical source of truth",
|
||||
"body": "The GitHub repository contains the skill source, installer CLI, user docs, plugin mirrors, bundles, workflows, and release history."
|
||||
},
|
||||
{
|
||||
"heading": "Searchable catalog",
|
||||
"body": "The hosted catalog gives users a fast browsing layer over the repository so they can discover skills before installing them."
|
||||
},
|
||||
{
|
||||
"heading": "Reusable across agents",
|
||||
"body": "The same library supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related assistant runtimes."
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"label": "Open GitHub source",
|
||||
"href": "https://github.com/sickn33/antigravity-awesome-skills"
|
||||
},
|
||||
{
|
||||
"label": "Install from npm",
|
||||
"href": "https://www.npmjs.com/package/antigravity-awesome-skills"
|
||||
},
|
||||
{
|
||||
"label": "Browse all skills",
|
||||
"to": "/"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "antigravity-plugins",
|
||||
"title": "Antigravity Plugins | Specialized AI coding workflow packs",
|
||||
"description": "Compare Antigravity-compatible plugin packs for web apps, security, data analytics, documents, DevOps, QA, OSS maintenance, and agent workflows.",
|
||||
"eyebrow": "Plugin packs",
|
||||
"h1": "Antigravity plugins for focused agent workflows",
|
||||
"summary": "Specialized plugins package the right skills for a domain so users can start with a narrow, practical surface instead of the full library.",
|
||||
"primaryIntent": "Find Antigravity plugin packs and focused AI workflow distributions.",
|
||||
"keywords": [
|
||||
"Antigravity plugins",
|
||||
"Antigravity plugin",
|
||||
"AI coding plugins",
|
||||
"specialized skill plugins"
|
||||
],
|
||||
"relatedTerms": [
|
||||
"plugin",
|
||||
"plugins",
|
||||
"bundle",
|
||||
"pack",
|
||||
"security",
|
||||
"frontend",
|
||||
"backend",
|
||||
"data",
|
||||
"devops",
|
||||
"qa",
|
||||
"testing",
|
||||
"mobile",
|
||||
"documents",
|
||||
"web app"
|
||||
],
|
||||
"relatedCategories": [
|
||||
"backend",
|
||||
"data",
|
||||
"devops",
|
||||
"document-processing",
|
||||
"frontend",
|
||||
"mobile",
|
||||
"security",
|
||||
"test-automation",
|
||||
"testing",
|
||||
"web-development"
|
||||
],
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Focused distributions",
|
||||
"body": "Plugin packs group related skills for domains such as frontend, security, data, documents, DevOps, QA, mobile, and agent systems."
|
||||
},
|
||||
{
|
||||
"heading": "Lower activation cost",
|
||||
"body": "A plugin gives the assistant a tighter instruction set, reducing noise when the full catalog is more than the current task needs."
|
||||
},
|
||||
{
|
||||
"heading": "Still backed by the full repo",
|
||||
"body": "Each plugin remains part of the larger Antigravity Awesome Skills repository and can be traced back to canonical skill sources."
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"label": "Compare all plugin packs",
|
||||
"to": "/plugins"
|
||||
},
|
||||
{
|
||||
"label": "Open GitHub plugins",
|
||||
"href": "https://github.com/sickn33/antigravity-awesome-skills/tree/main/plugins"
|
||||
},
|
||||
{
|
||||
"label": "Read plugin docs",
|
||||
"href": "https://github.com/sickn33/antigravity-awesome-skills/blob/main/docs/users/plugins.md"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"slug": "skills-para-antigravity",
|
||||
"title": "Skills para Antigravity | Installable AI coding skills",
|
||||
"description": "Find skills para Antigravity in the Antigravity Awesome Skills GitHub library, including installable playbooks for coding agents, plugins, bundles, and workflows.",
|
||||
"eyebrow": "International search",
|
||||
"h1": "Skills para Antigravity and AI coding agents",
|
||||
"summary": "A practical entry point for users searching for skills para Antigravity, Antigravity skills, or GitHub playbooks for AI coding assistants.",
|
||||
"primaryIntent": "Serve international searches for skills para Antigravity.",
|
||||
"keywords": [
|
||||
"skills para Antigravity",
|
||||
"Antigravity skills",
|
||||
"skills Antigravity GitHub",
|
||||
"AI coding assistant skills"
|
||||
],
|
||||
"relatedTerms": [
|
||||
"para",
|
||||
"espanol",
|
||||
"español",
|
||||
"portugues",
|
||||
"portuguese",
|
||||
"spanish",
|
||||
"international",
|
||||
"antigravity",
|
||||
"assistant"
|
||||
],
|
||||
"relatedCategories": [
|
||||
"andruia",
|
||||
"business-strategy",
|
||||
"content",
|
||||
"education",
|
||||
"leiloeiro",
|
||||
"marketing",
|
||||
"research",
|
||||
"writing"
|
||||
],
|
||||
"sections": [
|
||||
{
|
||||
"heading": "Installable skill playbooks",
|
||||
"body": "The repository provides reusable SKILL.md instructions that help AI coding assistants plan, implement, test, review, document, and ship work."
|
||||
},
|
||||
{
|
||||
"heading": "Works beyond one host",
|
||||
"body": "Antigravity is one target surface, but the library also supports other assistant runtimes through direct installs and plugin distributions."
|
||||
},
|
||||
{
|
||||
"heading": "Start with the catalog",
|
||||
"body": "Use the hosted catalog to compare skills and plugin packs, then open the GitHub repository for installation and source details."
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{
|
||||
"label": "Browse the catalog",
|
||||
"to": "/"
|
||||
},
|
||||
{
|
||||
"label": "Open the GitHub repository",
|
||||
"href": "https://github.com/sickn33/antigravity-awesome-skills"
|
||||
},
|
||||
{
|
||||
"label": "Compare plugins",
|
||||
"to": "/plugins"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,105 @@
|
||||
import rawLandingPages from './seoLandingPages.json';
|
||||
import type { Skill } from '../types';
|
||||
import type { SeoLandingPage } from '../utils/seo';
|
||||
|
||||
export const seoLandingPages = rawLandingPages as SeoLandingPage[];
|
||||
|
||||
export function getSeoLandingPage(slug: string | undefined): SeoLandingPage | undefined {
|
||||
if (!slug) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return seoLandingPages.find((page) => page.slug === slug);
|
||||
}
|
||||
|
||||
function normalize(value: string | undefined): string {
|
||||
return (value || '')
|
||||
.normalize('NFD')
|
||||
.replace(/[\u0300-\u036f]/g, '')
|
||||
.toLowerCase()
|
||||
.replace(/[^a-z0-9]+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function getPageMatchTerms(page: SeoLandingPage): string[] {
|
||||
return [
|
||||
page.slug,
|
||||
page.eyebrow,
|
||||
page.h1,
|
||||
page.summary,
|
||||
page.primaryIntent,
|
||||
...page.keywords,
|
||||
...(page.relatedTerms || []),
|
||||
];
|
||||
}
|
||||
|
||||
function scoreTopicForSkill(page: SeoLandingPage, skill: Skill): number {
|
||||
const haystack = normalize([
|
||||
skill.id,
|
||||
skill.name,
|
||||
skill.description,
|
||||
skill.category,
|
||||
skill.source,
|
||||
skill.path,
|
||||
].filter(Boolean).join(' '));
|
||||
const category = normalize(skill.category);
|
||||
const relatedCategories = (page.relatedCategories || []).map(normalize);
|
||||
let score = relatedCategories.includes(category) ? 12 : 0;
|
||||
|
||||
for (const term of getPageMatchTerms(page)) {
|
||||
const normalizedTerm = normalize(term);
|
||||
|
||||
if (!normalizedTerm || normalizedTerm.length < 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (haystack.includes(normalizedTerm)) {
|
||||
score += Math.min(12, 3 + normalizedTerm.split(' ').length * 2);
|
||||
continue;
|
||||
}
|
||||
|
||||
const matchedTokens = normalizedTerm
|
||||
.split(' ')
|
||||
.filter((token) => token.length >= 4 && haystack.includes(token));
|
||||
|
||||
score += Math.min(6, matchedTokens.length);
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
export function getRelatedSeoLandingPagesForSkill(skill: Skill, limit = 3): SeoLandingPage[] {
|
||||
const maxItems = Math.max(0, limit);
|
||||
|
||||
if (maxItems === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const scoredPages = seoLandingPages
|
||||
.map((page, index) => ({
|
||||
page,
|
||||
index,
|
||||
score: scoreTopicForSkill(page, skill),
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
if (a.score !== b.score) {
|
||||
return b.score - a.score;
|
||||
}
|
||||
|
||||
return a.index - b.index;
|
||||
});
|
||||
|
||||
const selected = scoredPages.filter(({ score }) => score > 0).map(({ page }) => page);
|
||||
|
||||
for (const { page } of scoredPages) {
|
||||
if (selected.length >= maxItems) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!selected.includes(page)) {
|
||||
selected.push(page);
|
||||
}
|
||||
}
|
||||
|
||||
return selected.slice(0, maxItems);
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import type { SyncMessage, CategoryStats } from '../types';
|
||||
import { usePageMeta } from '../hooks/usePageMeta';
|
||||
import { buildHomeMeta, getHomeFaqItems } from '../utils/seo';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { seoLandingPages } from '../data/seoLandingPages';
|
||||
|
||||
const conceptCards = [
|
||||
{
|
||||
@@ -52,6 +53,11 @@ const integrationGuides = [
|
||||
href: 'https://github.com/sickn33/antigravity-awesome-skills/blob/main/docs/users/gemini-cli-skills.md',
|
||||
body: 'A broad starting point for engineering, agent systems, integrations, and applied AI workflows.',
|
||||
},
|
||||
{
|
||||
name: 'Antigravity',
|
||||
href: 'https://github.com/sickn33/antigravity-awesome-skills#choose-your-tool',
|
||||
body: 'Installer targets for Antigravity IDE and Antigravity CLI, with reduced activation paths when the full library is too broad.',
|
||||
},
|
||||
] as const;
|
||||
|
||||
const syncFeatureEnabled = (
|
||||
@@ -173,9 +179,9 @@ export function Home(): React.ReactElement {
|
||||
Build agent workflows with production-grade skill playbooks
|
||||
</h1>
|
||||
<p className="mt-4 max-w-4xl text-sm leading-relaxed text-slate-600 sm:text-base dark:text-slate-300">
|
||||
Antigravity Awesome Skills is a curated catalog for the official GitHub repository of installable
|
||||
capabilities for AI assistants. Search fast, shortlist by category, and launch your first tested
|
||||
workflow from one focused workspace.
|
||||
Antigravity Awesome Skills is the searchable catalog for the official GitHub repository of installable
|
||||
AI agent skills, Antigravity CLI playbooks, specialized plugins, bundles, and workflows. Search fast,
|
||||
shortlist by category, and launch your first tested workflow from one focused workspace.
|
||||
</p>
|
||||
|
||||
<div className="mt-6 flex flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-stretch">
|
||||
@@ -215,6 +221,12 @@ export function Home(): React.ReactElement {
|
||||
>
|
||||
Compare specialized plugins
|
||||
</Link>
|
||||
<Link
|
||||
to="/topics/github-ai-skills-repository"
|
||||
className="inline-flex items-center justify-center rounded-lg border border-slate-400/80 bg-white/80 px-4 py-2.5 text-sm font-semibold text-slate-900 shadow-[inset_0_1px_0_rgba(255,255,255,0.9),0_10px_20px_-16px_rgba(15,23,42,0.7)] transition-colors hover:border-slate-500 hover:bg-slate-100 dark:border-slate-600 dark:bg-slate-800/70 dark:text-slate-100 dark:hover:bg-slate-700"
|
||||
>
|
||||
GitHub skills guide
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="mt-5 flex flex-wrap items-center gap-2 text-xs text-slate-500 dark:text-slate-400">
|
||||
@@ -436,6 +448,33 @@ export function Home(): React.ReactElement {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm sm:p-7 dark:border-slate-800 dark:bg-slate-900">
|
||||
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
|
||||
Search Topics
|
||||
</p>
|
||||
<h2 className="text-2xl font-bold tracking-tight text-slate-900 dark:text-slate-100">
|
||||
Direct entry points for high-intent searches
|
||||
</h2>
|
||||
<p className="mt-3 max-w-4xl text-sm leading-relaxed text-slate-600 sm:text-base dark:text-slate-300">
|
||||
These guides map common discovery queries to the right catalog surface, GitHub source, and plugin or installer path.
|
||||
</p>
|
||||
<div className="mt-6 grid gap-4 md:grid-cols-2">
|
||||
{seoLandingPages.map((page) => (
|
||||
<Link
|
||||
key={page.slug}
|
||||
to={`/topics/${page.slug}`}
|
||||
className="rounded-xl border border-slate-200 bg-gradient-to-br from-white to-slate-50 p-4 transition-colors hover:border-slate-400 dark:border-slate-800 dark:from-slate-900 dark:to-slate-950 dark:hover:border-slate-600"
|
||||
>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400">
|
||||
{page.eyebrow}
|
||||
</p>
|
||||
<h3 className="mt-2 text-base font-semibold text-slate-900 dark:text-slate-100">{page.h1}</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-slate-600 dark:text-slate-300">{page.summary}</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl border border-slate-200/80 bg-white p-6 shadow-sm sm:p-7 dark:border-slate-800 dark:bg-slate-900">
|
||||
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
|
||||
Quick FAQ
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useSkills } from '../context/SkillContext';
|
||||
import { usePageMeta } from '../hooks/usePageMeta';
|
||||
import { buildSkillFallbackMeta, buildSkillMeta, selectTopSkills } from '../utils/seo';
|
||||
import { getSkillMarkdownCandidateUrls } from '../utils/publicAssetUrls';
|
||||
import { getRelatedSeoLandingPagesForSkill } from '../data/seoLandingPages';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import rehypeHighlight from 'rehype-highlight';
|
||||
|
||||
@@ -100,6 +101,10 @@ export function SkillDetail(): React.ReactElement {
|
||||
const communityCount = useMemo(() => (id ? stars[id] || 0 : 0), [stars, id]);
|
||||
const { frontmatter, body: markdownBody } = useMemo(() => splitFrontmatter(content), [content]);
|
||||
const frontmatterRows = useMemo(() => parseFrontmatterRows(frontmatter), [frontmatter]);
|
||||
const relatedTopicPages = useMemo(
|
||||
() => skill ? getRelatedSeoLandingPagesForSkill(skill) : [],
|
||||
[skill],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (contextLoading || !skill) return;
|
||||
@@ -337,6 +342,49 @@ export function SkillDetail(): React.ReactElement {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{relatedTopicPages.length > 0 && (
|
||||
<section className="mb-8 rounded-3xl border border-slate-200/80 bg-white p-6 shadow-sm dark:border-slate-800 dark:bg-slate-900 sm:p-7">
|
||||
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
|
||||
Related topic guides
|
||||
</p>
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-end md:justify-between">
|
||||
<div>
|
||||
<h2 className="text-2xl font-bold tracking-tight text-slate-900 dark:text-slate-100">
|
||||
Place @{skill.name} in the larger AAS catalog
|
||||
</h2>
|
||||
<p className="mt-2 max-w-3xl text-sm leading-relaxed text-slate-600 dark:text-slate-300">
|
||||
These entry points connect this skill to installable Antigravity skills, the GitHub repository, and focused plugin packs.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
to="/topics/github-ai-skills-repository"
|
||||
className="inline-flex items-center justify-center rounded-lg border border-slate-300 px-4 py-2.5 text-sm font-semibold text-slate-800 transition-colors hover:bg-slate-100 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
>
|
||||
GitHub skills guide
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mt-5 grid gap-3 md:grid-cols-3">
|
||||
{relatedTopicPages.map((page) => (
|
||||
<Link
|
||||
key={page.slug}
|
||||
to={`/topics/${page.slug}`}
|
||||
className="rounded-xl border border-slate-200 bg-gradient-to-br from-white to-slate-50 p-4 transition-colors hover:border-slate-400 dark:border-slate-800 dark:from-slate-950 dark:to-slate-900 dark:hover:border-slate-600"
|
||||
>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-slate-500 dark:text-slate-400">
|
||||
{page.eyebrow}
|
||||
</p>
|
||||
<h3 className="mt-2 text-base font-semibold text-slate-900 dark:text-slate-100">
|
||||
{page.h1}
|
||||
</h3>
|
||||
<p className="mt-2 text-sm leading-relaxed text-slate-600 dark:text-slate-300">
|
||||
{page.primaryIntent}
|
||||
</p>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
<div className="overflow-hidden rounded-3xl border border-slate-200/80 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
||||
<div className="p-6 sm:p-8">
|
||||
{frontmatterRows.length > 0 && (
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
import { Link, useParams } from 'react-router-dom';
|
||||
import { Icon } from '../components/ui/Icon';
|
||||
import { getSeoLandingPage, seoLandingPages } from '../data/seoLandingPages';
|
||||
import { usePageMeta } from '../hooks/usePageMeta';
|
||||
import { buildTopicLandingFallbackMeta, buildTopicLandingMeta } from '../utils/seo';
|
||||
|
||||
export function TopicLanding(): React.ReactElement {
|
||||
const { slug } = useParams<{ slug: string }>();
|
||||
const page = getSeoLandingPage(slug);
|
||||
|
||||
usePageMeta(page ? buildTopicLandingMeta(page) : buildTopicLandingFallbackMeta(slug));
|
||||
|
||||
if (!page) {
|
||||
return (
|
||||
<div className="px-6 py-14 text-center sm:px-8">
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
|
||||
Topic guide
|
||||
</p>
|
||||
<h1 className="mt-3 text-2xl font-bold tracking-normal text-slate-900 dark:text-slate-100">
|
||||
Topic guide not found
|
||||
</h1>
|
||||
<p className="mx-auto mt-3 max-w-2xl text-sm leading-relaxed text-slate-600 dark:text-slate-300">
|
||||
This catalog guide is not available. Browse the current topic pages or return to the full skills catalog.
|
||||
</p>
|
||||
<div className="mt-6 flex flex-wrap justify-center gap-3">
|
||||
<Link
|
||||
to="/"
|
||||
className="inline-flex items-center justify-center rounded-lg bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white"
|
||||
>
|
||||
Browse all skills
|
||||
</Link>
|
||||
{seoLandingPages.slice(0, 2).map((landing) => (
|
||||
<Link
|
||||
key={landing.slug}
|
||||
to={`/topics/${landing.slug}`}
|
||||
className="inline-flex items-center justify-center rounded-lg border border-slate-300 px-4 py-2.5 text-sm font-semibold text-slate-800 transition-colors hover:bg-slate-100 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
>
|
||||
{landing.eyebrow}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const relatedTopicPages = seoLandingPages.filter((landing) => landing.slug !== page.slug).slice(0, 3);
|
||||
|
||||
return (
|
||||
<article className="space-y-10 px-6 py-8 sm:px-8 lg:px-10">
|
||||
<section className="grid gap-8 lg:grid-cols-[minmax(0,1fr)_22rem] lg:items-start">
|
||||
<div>
|
||||
<p className="mb-3 text-xs font-semibold uppercase tracking-[0.2em] text-slate-500 dark:text-slate-400">
|
||||
{page.eyebrow}
|
||||
</p>
|
||||
<h1 className="max-w-3xl text-3xl font-bold tracking-normal text-slate-900 sm:text-5xl sm:leading-tight dark:text-slate-100">
|
||||
{page.h1}
|
||||
</h1>
|
||||
<p className="mt-5 max-w-3xl text-base leading-relaxed text-slate-600 dark:text-slate-300">
|
||||
{page.summary}
|
||||
</p>
|
||||
<p className="mt-4 max-w-3xl text-sm leading-relaxed text-slate-500 dark:text-slate-400">
|
||||
{page.primaryIntent}
|
||||
</p>
|
||||
<div className="mt-6 flex flex-wrap gap-3">
|
||||
{page.links.map((link) => link.to ? (
|
||||
<Link
|
||||
key={`${link.label}-${link.to}`}
|
||||
to={link.to}
|
||||
className="inline-flex items-center justify-center rounded-lg border border-slate-300 px-4 py-2.5 text-sm font-semibold text-slate-800 transition-colors hover:bg-slate-100 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
>
|
||||
{link.label}
|
||||
</Link>
|
||||
) : (
|
||||
<a
|
||||
key={`${link.label}-${link.href}`}
|
||||
href={link.href}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex items-center justify-center rounded-lg border border-slate-300 px-4 py-2.5 text-sm font-semibold text-slate-800 transition-colors hover:bg-slate-100 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
>
|
||||
{link.label}
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<aside className="rounded-2xl border border-slate-200 bg-slate-50 p-5 dark:border-slate-800 dark:bg-slate-950">
|
||||
<h2 className="text-base font-semibold tracking-normal text-slate-900 dark:text-slate-100">
|
||||
Search intent covered
|
||||
</h2>
|
||||
<div className="mt-4 flex flex-wrap gap-2">
|
||||
{page.keywords.map((keyword) => (
|
||||
<span
|
||||
key={keyword}
|
||||
className="rounded-full border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200"
|
||||
>
|
||||
{keyword}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</aside>
|
||||
</section>
|
||||
|
||||
<section className="grid gap-4 md:grid-cols-3">
|
||||
{page.sections.map((section) => (
|
||||
<article
|
||||
key={section.heading}
|
||||
className="rounded-2xl border border-slate-200 bg-gradient-to-br from-white to-slate-50 p-5 dark:border-slate-800 dark:from-slate-900 dark:to-slate-950"
|
||||
>
|
||||
<h2 className="text-base font-semibold tracking-normal text-slate-900 dark:text-slate-100">
|
||||
{section.heading}
|
||||
</h2>
|
||||
<p className="mt-3 text-sm leading-relaxed text-slate-600 dark:text-slate-300">
|
||||
{section.body}
|
||||
</p>
|
||||
</article>
|
||||
))}
|
||||
</section>
|
||||
|
||||
<section className="rounded-2xl border border-slate-200 bg-white p-5 dark:border-slate-800 dark:bg-slate-900">
|
||||
<div className="flex flex-col gap-4 md:flex-row md:items-center md:justify-between">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold tracking-normal text-slate-900 dark:text-slate-100">
|
||||
Continue exploring the catalog
|
||||
</h2>
|
||||
<p className="mt-2 text-sm leading-relaxed text-slate-600 dark:text-slate-300">
|
||||
The topic pages are entry points. The full catalog and plugin index remain the fastest way to compare the live skill library.
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
to="/"
|
||||
className="inline-flex items-center justify-center gap-2 rounded-lg bg-slate-900 px-4 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-slate-800 dark:bg-slate-100 dark:text-slate-900 dark:hover:bg-white"
|
||||
>
|
||||
<Icon name="search" size={16} className="h-4 w-4" />
|
||||
Search all skills
|
||||
</Link>
|
||||
</div>
|
||||
<div className="mt-6 border-t border-slate-200 pt-5 dark:border-slate-800">
|
||||
<h2 className="text-base font-semibold tracking-normal text-slate-900 dark:text-slate-100">
|
||||
Related topic guides
|
||||
</h2>
|
||||
<div className="mt-3 flex flex-wrap gap-3">
|
||||
{relatedTopicPages.map((landing) => (
|
||||
<Link
|
||||
key={landing.slug}
|
||||
to={`/topics/${landing.slug}`}
|
||||
className="inline-flex items-center justify-center rounded-lg border border-slate-300 px-3.5 py-2 text-sm font-semibold text-slate-800 transition-colors hover:bg-slate-100 dark:border-slate-700 dark:text-slate-200 dark:hover:bg-slate-800"
|
||||
>
|
||||
{landing.h1}
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
||||
export default TopicLanding;
|
||||
@@ -104,6 +104,11 @@ describe('SkillDetail', () => {
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('@react-patterns')).toBeInTheDocument();
|
||||
expect(screen.getByText('React design patterns and best practices')).toBeInTheDocument();
|
||||
expect(screen.getByText(/Related topic guides/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: /Antigravity plugins/i })).toHaveAttribute(
|
||||
'href',
|
||||
'/topics/antigravity-plugins',
|
||||
);
|
||||
expect(screen.getByTestId('markdown-content')).toHaveTextContent('This is the skill content.');
|
||||
expect(document.title).toContain('react-patterns');
|
||||
expect(document.querySelector('meta[name="twitter:title"]')).toHaveAttribute(
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { screen, waitFor } from '@testing-library/react';
|
||||
import { TopicLanding } from '../TopicLanding';
|
||||
import { renderWithRouter } from '../../utils/testUtils';
|
||||
|
||||
describe('TopicLanding', () => {
|
||||
it('renders an SEO topic page and sets metadata', async () => {
|
||||
renderWithRouter(<TopicLanding />, {
|
||||
route: '/topics/antigravity-cli-skills',
|
||||
path: '/topics/:slug',
|
||||
useProvider: false,
|
||||
});
|
||||
|
||||
expect(screen.getByRole('heading', { level: 1, name: /Antigravity CLI skills/i })).toBeInTheDocument();
|
||||
expect(screen.getByText(/Search intent covered/i)).toBeInTheDocument();
|
||||
expect(screen.getByText(/Related topic guides/i)).toBeInTheDocument();
|
||||
expect(screen.getByRole('link', { name: /GitHub repository for installable AI agent skills/i })).toHaveAttribute(
|
||||
'href',
|
||||
'/topics/github-ai-skills-repository',
|
||||
);
|
||||
expect(screen.getAllByText(/Antigravity CLI skills/i).length).toBeGreaterThan(0);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.title).toContain('Antigravity CLI Skills');
|
||||
});
|
||||
|
||||
expect(document.querySelector('meta[name="description"]')).toHaveAttribute(
|
||||
'content',
|
||||
expect.stringContaining('Install Antigravity CLI skills'),
|
||||
);
|
||||
});
|
||||
|
||||
it('renders a fallback for unknown topic slugs', async () => {
|
||||
renderWithRouter(<TopicLanding />, {
|
||||
route: '/topics/not-real',
|
||||
path: '/topics/:slug',
|
||||
useProvider: false,
|
||||
});
|
||||
|
||||
expect(screen.getByText(/Topic guide not found/i)).toBeInTheDocument();
|
||||
|
||||
await waitFor(() => {
|
||||
expect(document.title).toContain('Topic guide loading');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -5,12 +5,14 @@ import {
|
||||
buildHomeMeta,
|
||||
buildSkillFallbackMeta,
|
||||
buildSkillMeta,
|
||||
buildTopicLandingMeta,
|
||||
getCanonicalUrl,
|
||||
isTopSkill,
|
||||
selectTopSkills,
|
||||
setPageMeta,
|
||||
toCanonicalPath,
|
||||
} from '../seo';
|
||||
import { seoLandingPages } from '../../data/seoLandingPages';
|
||||
|
||||
function createSkill(overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
@@ -28,8 +30,9 @@ describe('SEO helpers', () => {
|
||||
it('builds homepage metadata with the canonical catalog message', () => {
|
||||
const meta = buildHomeMeta(10);
|
||||
|
||||
expect(meta.title).toContain('10+ AI coding skills and plugins');
|
||||
expect(meta.description).toContain('10 installable agentic skills');
|
||||
expect(meta.title).toContain('Antigravity Awesome Skills GitHub');
|
||||
expect(meta.title).toContain('10+ AI coding skills');
|
||||
expect(meta.description).toContain('GitHub library of 10+ installable agentic skills');
|
||||
expect(meta.canonicalPath).toBe('/');
|
||||
expect(meta.ogTitle).toBe(meta.title);
|
||||
expect(meta.ogImage).toBe(DEFAULT_SOCIAL_IMAGE);
|
||||
@@ -80,6 +83,19 @@ describe('SEO helpers', () => {
|
||||
expect(typeof meta.jsonLd).toBe('function');
|
||||
});
|
||||
|
||||
it('builds topic landing metadata for high-intent search pages', () => {
|
||||
const topic = seoLandingPages.find((page) => page.slug === 'github-ai-skills-repository');
|
||||
expect(topic).toBeDefined();
|
||||
|
||||
const meta = buildTopicLandingMeta(topic!);
|
||||
|
||||
expect(meta.title).toContain('GitHub AI Skills Repository');
|
||||
expect(meta.description).toContain('GitHub repository');
|
||||
expect(meta.canonicalPath).toBe('/topics/github-ai-skills-repository');
|
||||
expect(meta.ogImage).toBe(DEFAULT_SOCIAL_IMAGE);
|
||||
expect(typeof meta.jsonLd).toBe('function');
|
||||
});
|
||||
|
||||
it('returns coherent fallback metadata for unresolved skill ids', () => {
|
||||
const meta = buildSkillFallbackMeta('sample-skill');
|
||||
|
||||
|
||||
@@ -6,17 +6,49 @@ export const DEFAULT_SOCIAL_IMAGE = 'social-card.svg';
|
||||
const SITE_NAME = 'Antigravity Awesome Skills';
|
||||
const REPOSITORY_URL = 'https://github.com/sickn33/antigravity-awesome-skills';
|
||||
const HOSTED_CATALOG_URL = 'https://sickn33.github.io/antigravity-awesome-skills/';
|
||||
const TOPIC_ROUTE_PREFIX = '/topics';
|
||||
|
||||
export interface SeoLandingPageLink {
|
||||
label: string;
|
||||
href?: string;
|
||||
to?: string;
|
||||
}
|
||||
|
||||
export interface SeoLandingPageSection {
|
||||
heading: string;
|
||||
body: string;
|
||||
}
|
||||
|
||||
export interface SeoLandingPage {
|
||||
slug: string;
|
||||
title: string;
|
||||
description: string;
|
||||
eyebrow: string;
|
||||
h1: string;
|
||||
summary: string;
|
||||
primaryIntent: string;
|
||||
keywords: string[];
|
||||
relatedTerms?: string[];
|
||||
relatedCategories?: string[];
|
||||
sections: SeoLandingPageSection[];
|
||||
links: SeoLandingPageLink[];
|
||||
}
|
||||
const FAQ_ITEMS = [
|
||||
{
|
||||
question: 'What is Antigravity Awesome Skills?',
|
||||
answer:
|
||||
'Antigravity Awesome Skills is an installable GitHub library of 1,550+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable catalog.',
|
||||
'Antigravity Awesome Skills is an installable GitHub library of 1,700+ reusable SKILL.md playbooks for AI coding assistants. It supports Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and related hosts through direct skill installs, specialized plugins, bundles, workflows, and a searchable catalog.',
|
||||
},
|
||||
{
|
||||
question: 'How do I install Antigravity Awesome Skills?',
|
||||
answer:
|
||||
'Install the library with npx antigravity-awesome-skills. Use tool-specific flags such as --codex, --cursor, --gemini, --claude, or --antigravity when you want the installer to target a specific skills directory already used by your assistant runtime.',
|
||||
},
|
||||
{
|
||||
question: 'Is Antigravity Awesome Skills a GitHub repository?',
|
||||
answer:
|
||||
'Yes. The GitHub repository at https://github.com/sickn33/antigravity-awesome-skills is the canonical source for the skill library, installer, specialized plugins, bundles, workflows, and documentation. The hosted catalog is the searchable browsing surface for that repository.',
|
||||
},
|
||||
{
|
||||
question: 'What are AAS specialized plugins?',
|
||||
answer:
|
||||
@@ -139,6 +171,9 @@ function buildSoftwareSourceCodeSchema(canonicalUrl: string, visibleCount: numbe
|
||||
'Cursor skills',
|
||||
'Gemini CLI skills',
|
||||
'Antigravity skills',
|
||||
'Antigravity CLI skills',
|
||||
'GitHub AI skills repository',
|
||||
'AI agent skills GitHub',
|
||||
'specialized plugins',
|
||||
'SKILL.md',
|
||||
],
|
||||
@@ -320,11 +355,11 @@ export function buildHomeMeta(skillCount: number): SeoMeta {
|
||||
const visibleCount = Math.max(skillCount, 0);
|
||||
const visibleCountLabel = visibleCount > 0 ? `${visibleCount.toLocaleString('en-US')}+` : '';
|
||||
const title = visibleCount > 0
|
||||
? `Antigravity Awesome Skills | ${visibleCountLabel} AI coding skills and plugins`
|
||||
: 'Antigravity Awesome Skills | AI coding skills and plugins';
|
||||
? `Antigravity Awesome Skills GitHub | ${visibleCountLabel} AI coding skills`
|
||||
: 'Antigravity Awesome Skills GitHub | AI coding skills';
|
||||
const description = visibleCount > 0
|
||||
? `Explore ${visibleCount.toLocaleString('en-US')} installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.`
|
||||
: 'Explore installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.';
|
||||
? `Explore the GitHub library of ${visibleCountLabel} installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.`
|
||||
: 'Explore the GitHub library of installable agentic skills, specialized plugins, bundles, and workflows for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and other AI coding assistants.';
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
@@ -389,6 +424,81 @@ export function buildPluginsMeta(pluginCount: number): SeoMeta {
|
||||
};
|
||||
}
|
||||
|
||||
export function buildTopicLandingMeta(page: SeoLandingPage): SeoMeta {
|
||||
const canonicalPath = `${TOPIC_ROUTE_PREFIX}/${page.slug}`;
|
||||
const keywords = page.keywords.join(', ');
|
||||
|
||||
return {
|
||||
title: page.title,
|
||||
description: page.description,
|
||||
canonicalPath,
|
||||
ogTitle: page.title,
|
||||
ogDescription: page.description,
|
||||
ogImage: DEFAULT_SOCIAL_IMAGE,
|
||||
twitterCard: 'summary_large_image',
|
||||
jsonLd: (canonicalUrl: string) => [
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'WebPage',
|
||||
name: page.h1,
|
||||
headline: page.h1,
|
||||
description: page.description,
|
||||
url: canonicalUrl,
|
||||
isPartOf: buildWebSiteSchema(canonicalUrl),
|
||||
about: buildSoftwareSourceCodeSchema(canonicalUrl, 0),
|
||||
keywords,
|
||||
mainEntity: {
|
||||
'@type': 'ItemList',
|
||||
name: `${page.eyebrow} topics`,
|
||||
itemListElement: page.sections.map((section, index) => ({
|
||||
'@type': 'ListItem',
|
||||
position: index + 1,
|
||||
name: section.heading,
|
||||
description: section.body,
|
||||
})),
|
||||
},
|
||||
},
|
||||
{
|
||||
'@context': 'https://schema.org',
|
||||
'@type': 'BreadcrumbList',
|
||||
itemListElement: [
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 1,
|
||||
name: SITE_NAME,
|
||||
item: HOSTED_CATALOG_URL,
|
||||
},
|
||||
{
|
||||
'@type': 'ListItem',
|
||||
position: 2,
|
||||
name: page.h1,
|
||||
item: canonicalUrl,
|
||||
},
|
||||
],
|
||||
},
|
||||
buildOrganizationSchema(),
|
||||
buildWebSiteSchema(canonicalUrl),
|
||||
buildSoftwareSourceCodeSchema(canonicalUrl, 0),
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
export function buildTopicLandingFallbackMeta(slug: string | undefined): SeoMeta {
|
||||
const safeSlug = encodeURIComponent((slug || 'topic').trim() || 'topic');
|
||||
const title = `Topic guide loading | ${SITE_NAME}`;
|
||||
const description = 'This Antigravity Awesome Skills topic guide is loading from the hosted catalog.';
|
||||
|
||||
return {
|
||||
title,
|
||||
description,
|
||||
canonicalPath: `${TOPIC_ROUTE_PREFIX}/${safeSlug}`,
|
||||
ogTitle: title,
|
||||
ogDescription: description,
|
||||
ogImage: DEFAULT_SOCIAL_IMAGE,
|
||||
twitterCard: 'summary',
|
||||
};
|
||||
}
|
||||
|
||||
export function buildSkillMeta(skill: Skill, isPriority = false, canonicalPath = '/'): SeoMeta {
|
||||
const safeName = skill.name || 'Unnamed skill';
|
||||
const safeDescription = skill.description || 'Installable AI skill';
|
||||
|
||||
Reference in New Issue
Block a user