📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-06-29 16:09:10 +00:00
parent 90c6c04c3f
commit f7f50d9fea
314 changed files with 31913 additions and 351 deletions
@@ -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';