📦 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
@@ -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>