diff --git a/app/robots.ts b/app/robots.ts new file mode 100644 index 0000000..3c91981 --- /dev/null +++ b/app/robots.ts @@ -0,0 +1,14 @@ +import type { MetadataRoute } from "next" + +export default function robots(): MetadataRoute.Robots { + return { + rules: { + userAgent: "*", + allow: "/", + // Transactional and machine-only routes carry no SEO value. + disallow: ["/api/", "/buy/"], + }, + sitemap: "https://solidstate.cc/sitemap.xml", + host: "https://solidstate.cc", + } +} diff --git a/app/sitemap.ts b/app/sitemap.ts new file mode 100644 index 0000000..b4109ba --- /dev/null +++ b/app/sitemap.ts @@ -0,0 +1,44 @@ +import type { MetadataRoute } from "next" +import { skills } from "@/lib/skills" +import { glossary } from "@/lib/glossary" + +const BASE = "https://solidstate.cc" + +// Static, indexable routes. Transactional pages (/buy*, /submit) and API +// routes are intentionally excluded — they carry no SEO value and are blocked +// in robots.ts. +const STATIC_PATHS = [ + { path: "/", priority: 1.0 }, + { path: "/skills", priority: 0.9 }, + { path: "/glossary", priority: 0.7 }, + { path: "/official", priority: 0.6 }, + { path: "/audits", priority: 0.5 }, + { path: "/manifesto", priority: 0.5 }, +] + +export default function sitemap(): MetadataRoute.Sitemap { + const now = new Date() + + const staticEntries: MetadataRoute.Sitemap = STATIC_PATHS.map(({ path, priority }) => ({ + url: `${BASE}${path}`, + lastModified: now, + changeFrequency: "weekly", + priority, + })) + + const skillEntries: MetadataRoute.Sitemap = skills.map((s) => ({ + url: `${BASE}/skills/${s.slug}`, + lastModified: s.createdAt ? new Date(s.createdAt) : now, + changeFrequency: "weekly", + priority: 0.8, + })) + + const glossaryEntries: MetadataRoute.Sitemap = glossary.map((t) => ({ + url: `${BASE}/glossary/${t.slug}`, + lastModified: now, + changeFrequency: "monthly", + priority: 0.4, + })) + + return [...staticEntries, ...skillEntries, ...glossaryEntries] +} diff --git a/app/skills/[slug]/page.tsx b/app/skills/[slug]/page.tsx index 277cd43..921a826 100644 --- a/app/skills/[slug]/page.tsx +++ b/app/skills/[slug]/page.tsx @@ -20,9 +20,26 @@ export async function generateMetadata({ params }: Props): Promise { const { slug } = await params const skill = getSkillBySlug(slug) if (!skill) return {} + + const canonical = `/skills/${skill.slug}` + const title = `${skill.name} — ${skill.author}` + return { title: skill.name, description: skill.description, + alternates: { canonical }, + openGraph: { + type: "article", + title, + description: skill.description, + url: canonical, + siteName: "Solid State", + }, + twitter: { + card: "summary_large_image", + title, + description: skill.description, + }, } } diff --git a/app/skills/page.tsx b/app/skills/page.tsx index dc42e6b..89a762f 100644 --- a/app/skills/page.tsx +++ b/app/skills/page.tsx @@ -3,9 +3,25 @@ import { Suspense } from "react" import { skills, CATEGORIES, PLATFORMS } from "@/lib/skills" import { SkillsBrowser } from "./SkillsBrowser" +const SKILLS_DESCRIPTION = + "Browse and filter AI agent skills for Claude, OpenClaw, NemoClaw, Antigravity, and any agent runtime." + export const metadata: Metadata = { title: "Browse Skills", - description: "Browse and filter AI agent skills for Claude, OpenClaw, NemoClaw, Antigravity, and any agent runtime.", + description: SKILLS_DESCRIPTION, + alternates: { canonical: "/skills" }, + openGraph: { + type: "website", + title: "Browse Skills | Solid State", + description: SKILLS_DESCRIPTION, + url: "/skills", + siteName: "Solid State", + }, + twitter: { + card: "summary_large_image", + title: "Browse Skills | Solid State", + description: SKILLS_DESCRIPTION, + }, } export default function SkillsPage() {