seo · git:20260906.5003aa5 · 2026-09-06 · sha256 213f86f69f55113d

seo git:20260906.5003aa5B

Immutable. This exact content is served forever at /api/v1/blob/213f86f69f55113d.

---
name: seo
description: Audit and bootstrap SEO for a web project — sitemap.xml, robots.txt, llms.txt, JSON-LD structured data, and meta tags. Use when launching a site or adding SEO to an existing one.
user-invocable: true
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
argument-hint: [project-path]
---

# SEO Bootstrapper

Audit a web project's SEO setup and generate everything missing.

**Argument**: `$ARGUMENTS` is the project path. If not provided, ask.

## Process

### 1. Scan what's already there

Run these checks in parallel:

```bash
# Check for existing SEO files
ls public/sitemap.xml public/robots.txt public/llms.txt 2>/dev/null
ls src/app/sitemap.* src/app/robots.* 2>/dev/null

# Check for meta tags in HTML
grep -l "og:title" *.html public/*.html 2>/dev/null

# Check for JSON-LD
grep -rl "application/ld+json" . 2>/dev/null

# Check framework
cat package.json | grep -E "(next|vite|astro)" 2>/dev/null
```

### 2. Ask for essentials (if missing)

- **Canonical domain** (e.g., `https://mytool.app`)
- **Site name** and **tagline**
- **Description** (160 char max for meta description)
- **Content type**: marketing site, blog, web app, article, or tool?
- **Organization info**: name, logo URL, social links (for JSON-LD)

### 3. Generate missing pieces

Use the framework-appropriate approach:

#### A. sitemap.xml

**Static HTML sites** — create `public/sitemap.xml`:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>https://[domain]/</loc>
    <lastmod>2026-04-16</lastmod>
    <changefreq>monthly</changefreq>
    <priority>1.0</priority>
  </url>
  <url>
    <loc>https://[domain]/articles/</loc>
    <lastmod>2026-04-16</lastmod>
    <changefreq>weekly</changefreq>
    <priority>0.8</priority>
  </url>
  <!-- Additional pages at 0.7 priority, monthly -->
</urlset>
```

**Next.js App Router** — create `src/app/sitemap.ts`:
```typescript
import type { MetadataRoute } from 'next';

export default function sitemap(): MetadataRoute.Sitemap {
  return [
    {
      url: 'https://[domain]',
      lastModified: new Date(),
      changeFrequency: 'monthly',
      priority: 1.0,
    },
    // Additional URLs
  ];
}
```

**Priority guide:**
- Homepage: 1.0
- Index pages (articles, products): 0.8
- Individual content pages: 0.7
- Utility pages (about, contact): 0.5

#### B. robots.txt

**Static sites** — create `public/robots.txt`:
```
User-agent: *
Allow: /

# AI crawlers
User-agent: GPTBot
Allow: /

User-agent: ClaudeBot
Allow: /

User-agent: Claude-SearchBot
Allow: /

User-agent: PerplexityBot
Allow: /

User-agent: Amazonbot
Allow: /

User-agent: CCBot
Allow: /

Sitemap: https://[domain]/sitemap.xml
```

**Next.js** — create `src/app/robots.ts`:
```typescript
import type { MetadataRoute } from 'next';

export default function robots(): MetadataRoute.Robots {
  return {
    rules: [
      { userAgent: '*', allow: '/', disallow: ['/api/'] },
      { userAgent: 'GPTBot', allow: '/' },
      { userAgent: 'ClaudeBot', allow: '/' },
      { userAgent: 'Claude-SearchBot', allow: '/' },
      { userAgent: 'PerplexityBot', allow: '/' },
    ],
    sitemap: 'https://[domain]/sitemap.xml',
  };
}
```

#### C. llms.txt

Create `public/llms.txt` (or root for Next.js):

```markdown
# [Site Name]

> [One-sentence tagline]

## What is [Site Name]?

[2-3 sentence description — what it does, who it's for]

## Key features

- **[Feature 1]**: [Description]
- **[Feature 2]**: [Description]
- **[Feature 3]**: [Description]

## Links

- Homepage: https://[domain]/
- Articles: https://[domain]/articles/
- Contact: https://[domain]/contact/

## About

[1-2 sentences about the creator/company]
```

Link it from the HTML head:
```html
<link rel="alternate" type="text/plain" href="/llms.txt" title="LLM-friendly content">
```

#### D. JSON-LD structured data

Choose the schema based on content type:

**Organization** (homepage of a product/company):
```html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Organization",
  "name": "[Name]",
  "url": "https://[domain]",
  "logo": "https://[domain]/logo.png",
  "sameAs": [
    "https://x.com/[handle]",
    "https://linkedin.com/in/[handle]"
  ]
}
</script>
```

**WebApplication** (a tool or app):
```html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "WebApplication",
  "name": "[Tool Name]",
  "url": "https://[domain]",
  "description": "[Description]",
  "applicationCategory": "BusinessApplication",
  "operatingSystem": "Web",
  "offers": {
    "@type": "Offer",
    "price": "0",
    "priceCurrency": "USD"
  }
}
</script>
```

**Article** (blog posts):
```html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Article",
      "headline": "[Title]",
      "description": "[Description]",
      "datePublished": "2026-04-16",
      "author": { "@type": "Person", "name": "[Author]", "url": "[url]" },
      "publisher": {
        "@type": "Organization",
        "name": "[Publisher]",
        "logo": { "@type": "ImageObject", "url": "[logo url]" }
      },
      "mainEntityOfPage": "https://[domain]/article-url"
    },
    {
      "@type": "BreadcrumbList",
      "itemListElement": [
        { "@type": "ListItem", "position": 1, "name": "Home", "item": "https://[domain]/" },
        { "@type": "ListItem", "position": 2, "name": "Articles", "item": "https://[domain]/articles/" }
      ]
    }
  ]
}
</script>
```

For Next.js, extract into a `<JsonLd>` component:
```typescript
// src/components/JsonLd.tsx
export function JsonLd({ data }: { data: Record<string, unknown> }) {
  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }}
    />
  );
}
```

#### E. Meta tags

Ensure every HTML page has:

```html
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>[Specific Page Title] — [Site Name]</title>
<meta name="description" content="[160 char description]">
<meta name="robots" content="index, follow, max-image-preview:large">

<!-- OG -->
<meta property="og:title" content="[Title]">
<meta property="og:description" content="[Description]">
<meta property="og:url" content="https://[domain]/[path]">
<meta property="og:image" content="https://[domain]/og-image.png">
<meta property="og:type" content="website">
<meta property="og:site_name" content="[Site Name]">

<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="[Title]">
<meta name="twitter:description" content="[Description]">
<meta name="twitter:image" content="https://[domain]/og-image.png">

<!-- Canonical -->
<link rel="canonical" href="https://[domain]/[path]">
```

For Next.js, use the `metadata` export in `layout.tsx`:
```typescript
export const metadata: Metadata = {
  metadataBase: new URL('https://[domain]'),
  title: { default: '[Site Name]', template: '%s — [Site Name]' },
  description: '[Description]',
  openGraph: {
    title: '[Site Name]',
    description: '[Description]',
    url: 'https://[domain]',
    siteName: '[Site Name]',
    images: ['/og-image.png'],
    type: 'website',
  },
  twitter: {
    card: 'summary_large_image',
    title: '[Site Name]',
    description: '[Description]',
    images: ['/og-image.png'],
  },
  alternates: { canonical: 'https://[domain]' },
};
```

### 4. Report what was done

Format as a checklist:

```
## SEO Setup: [Project Name]

### Generated
✓ public/sitemap.xml (12 URLs)
✓ public/robots.txt (with AI crawler allowlist)
✓ public/llms.txt
✓ JSON-LD: Organization schema added to layout

### Already present
✓ OG meta tags
✓ Canonical URL
✓ Twitter cards

### Needs attention
⚠ Missing og-image.png — run /og-image to generate
⚠ Logo URL is a placeholder — update to real logo
```

### 5. Remind about verification

After deploying, verify with:
- Google Rich Results Test: https://search.google.com/test/rich-results
- Facebook Sharing Debugger: https://developers.facebook.com/tools/debug/
- LinkedIn Post Inspector: https://www.linkedin.com/post-inspector/

## Rules
- Never invent content — if description, tagline, or features are missing, ask
- Always use absolute URLs in sitemap and JSON-LD (not relative paths)
- Explicitly allow AI crawlers (GPTBot, ClaudeBot, Claude-SearchBot, PerplexityBot) — AI search is now a real traffic source, so opt in unless the user says otherwise
- For blogs/articles, use the @graph array to combine Article + BreadcrumbList
- Don't add JSON-LD for content that doesn't exist yet (e.g., don't add Article schema to a homepage)
- Match framework patterns: don't add `public/sitemap.xml` to a Next.js project, use `src/app/sitemap.ts`
- Keep llms.txt concise (under 500 words) — it's for AI skim, not humans