技能创建器
skill-creator
创建、改进和评估自定义技能,把专业流程转化为可重复使用的 AI 能力。
enhance-web-seo【数据与研究】Audit and fix SEO for a web app. Use when "improve SEO", "SEO audit", "add meta tags", "add structured data", "sitemap", "canonical URLs", "Open Graph", "Google indexing", "rich results", or "why is my site not ranking".
独立 GitHub 快照 · 自动分类 · 以原作者文档为准
Degree of freedom: MIXED. Title/snippet judgment [HIGH freedom]; live probes, robots/sitemap, and re-measure [LOW freedom — run exactly].
Google does not rank pages it cannot read, understand, or trust. This skill finds every gap between your app and how search engines see it, then fixes them in order of impact.
Before browser work, read protocol-browser-anti-stall and apply it.
Detect: Next.js App Router; root
metadataonly; nositemap.ts;/robots.txt404. Measure: product pages inherit the home title; no canonical; no JSON-LD; LCP 3.1s. Fix:generateMetadataper product;app/sitemap.ts+ robots; Product JSON-LD; image dimensions. Re-measure: unique titles; OG image resolves; sitemap lists products; LCP < 2.5s.
Disallow: / or noindexaudit-bundle-size; AEO citations → plan-aeo-readinesspackage.json → Next.js Metadata API, remix-utils/seo, @vueuse/head, etc.
src/app/layout.tsx → existing metadata export (Next.js App Router)
src/app/head.tsx → legacy Head component
public/robots.txt → existing robots config
public/sitemap.xml → existing sitemap
Also check if a sitemap generator is configured (next-sitemap, astro-sitemap,
SvelteKit's @sveltejs/adapter-static sitemap, etc.).
For every public-facing route, run the following:
// eval after goto
const seo = await page.evaluate(() => ({
title: document.title,
description: document.querySelector('meta[name="description"]')?.content,
canonical: document.querySelector('link[rel="canonical"]')?.href,
og_title: document.querySelector('meta[property="og:title"]')?.content,
og_description: document.querySelector('meta[property="og:description"]')?.content,
og_image: document.querySelector('meta[property="og:image"]')?.content,
og_url: document.querySelector('meta[property="og:url"]')?.content,
twitter_card: document.querySelector('meta[name="twitter:card"]')?.content,
robots: document.querySelector('meta[name="robots"]')?.content,
jsonld: [...document.querySelectorAll('script[type="application/ld+json"]')]
.map(s => s.textContent),
}));
const headings = await page.evaluate(() =>
[...document.querySelectorAll('h1,h2,h3,h4,h5,h6')]
.map(h => ({ tag: h.tagName, text: h.textContent.trim().slice(0, 60) }))
);
Rules: exactly one <h1> per page; <h2> never skips to <h4> without <h3>.
const images = await page.evaluate(() =>
[...document.querySelectorAll('img')]
.filter(img => !img.alt || img.alt.trim() === '')
.map(img => img.src.slice(-60))
);
CWV (LCP, INP, CLS) is a ranking input. Measure field p75 via CrUX/PSI. Do not fix CWV here — hand off:
audit-performance §Loading Priority & Speculationaudit-bundle-sizeenhance-web-instant-nav
Record before/after p75 in this skill's re-measure step.goto to /robots.txt. Check:
Disallow: / unless intentional (private app)Sitemap: https://yourdomain.com/sitemap.xmlNavigate to /sitemap.xml or /sitemap-index.xml. Check:
/)<lastmod> dates are current, not hardcoded to a past dateValidate each JSON-LD block found in Phase 1a:
@context: "https://schema.org" present@type matches the page content (Article, Product, Organization, WebSite, BreadcrumbList, FAQPage, etc.)references/structured-data-types.md)<link rel="canonical"> pointing to its definitive URLFollow /research: Context7 for the framework's metadata, sitemap, and robots API; Firecrawl for current Google guidance on CWV, structured data, and rich results, dated to now. Anchor to the framework version actually installed.
Every public page needs a unique, descriptive title (50–60 chars) and meta description (150–160 chars).
Next.js App Router:
// app/page.tsx
export const metadata: Metadata = {
title: 'Specific Page Title — Brand Name',
description: 'One or two sentences that describe this page for someone scanning search results.',
};
Next.js dynamic routes:
export async function generateMetadata({ params }): Promise<Metadata> {
const item = await fetchItem(params.id);
return {
title: `${item.name} — Brand Name`,
description: item.summary,
openGraph: { title: item.name, description: item.summary, images: [item.coverImage] },
};
}
Required for social sharing previews. Every page needs at minimum:
openGraph: {
title: 'Page Title',
description: 'Page description',
url: 'https://yourdomain.com/page',
siteName: 'Brand Name',
images: [{ url: '/og-image.png', width: 1200, height: 630 }],
type: 'website', // or 'article' for blog posts
},
twitter: {
card: 'summary_large_image',
title: 'Page Title',
description: 'Page description',
images: ['/og-image.png'],
},
robots.txt (static file at public/robots.txt):
User-agent: *
Allow: /
Sitemap: https://yourdomain.com/sitemap.xml
Sitemap — use the framework's built-in generator where available:
app/sitemap.ts returning MetadataRoute.Sitemap+server.ts returning XML responsesitemap[.]xml.ts resource routeAdd to the relevant page layout:
// For a website home page
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'Brand Name',
url: 'https://yourdomain.com',
potentialAction: {
'@type': 'SearchAction',
target: 'https://yourdomain.com/search?q={search_term_string}',
'query-input': 'required name=search_term_string',
},
};
// Inject via: <script type="application/ld+json">{JSON.stringify(jsonLd)}</script>
See audit-bundle-size for JS bundle → LCP fixes.
For CLS: ensure images have explicit width/height, fonts use font-display: swap,
ads/embeds have reserved space.
After fixes, re-run Phase 1 checks on each modified page. Confirm:
document.title and meta description match the intentPer page:
- [ ] Unique descriptive title (50–60 chars)
- [ ] Meta description (150–160 chars), not cut off
- [ ] Exactly one <h1>
- [ ] Heading hierarchy correct (no skipped levels)
- [ ] All images have alt text
- [ ] Canonical URL present and correct
- [ ] OG title, description, image
- [ ] Twitter card meta
- [ ] JSON-LD structured data where relevant
Site-wide:
- [ ] robots.txt exists and correct
- [ ] Sitemap exists and up to date
- [ ] No important pages noindexed accidentally
- [ ] HTTPS enforced, no mixed content
- [ ] LCP < 2.5 s
- [ ] CLS < 0.1