Website Audit
Audit any website across 5 categories: AEO, GEO, SEO Technical, SEO On-Page, and Structured Data. Produces a scored, prioritised report with actionable fixes.
Arguments
/website-audit <domain> [categories...] [+refresh] [comparison-domain]
- domain (required): The site to audit (e.g.
example.com)
- categories (optional): Subset of
aeo, geo, seo-technical, seo-on-page, structured-data. Omit to audit all 5.
- +refresh: Force-refresh reference files even if <30 days old.
- comparison-domain: If a second domain is provided, run comparison mode.
Dependencies
Before starting, verify these are available. If any are missing, tell the user what to install and stop.
- Node.js 22+ (
node --version)
- Playwright CLI (
npx playwright --version) — if missing: npx playwright install chromium
- Python 3 (
python3 --version)
- Lighthouse (
lighthouse --version) — if missing: npm install -g lighthouse
- jq (
jq --version)
- Optional:
PERPLEXITY_API_KEY env var for citation checking
Audit Flow
Work through these phases in order. Each phase builds on the previous one's data.
Phase 0: Setup
- Parse arguments. Extract domain(s) and requested categories. Default to all 5 categories.
- Check dependencies. Run the version commands above. Stop on failure.
- Comparison mode? If two domains provided, set
COMPARISON_MODE=true. You will run the full audit on each domain, then produce a comparison report instead of two separate ones.
- Reference freshness. Check if any file in
references/ is >30 days old (use file modification time). If so, or if +refresh was requested, read references/refresh-guide.md and follow its instructions to update stale files. If all files are fresh and no +refresh, skip this.
Phase A: Crawl Technical Files + Homepage
Run these four tasks in parallel:
robots.txt + sitemap.xml — Fetch via curl:
curl -sL "https://{domain}/robots.txt" -o /tmp/audit-robots.txt
curl -sL "https://{domain}/sitemap.xml" -o /tmp/audit-sitemap.xml
Parse robots.txt for Disallow rules and AI bot directives. Parse sitemap for URL list.
Lighthouse — Spawn a background subagent:
Agent(lighthouse-runner):
model: haiku
prompt: |
Run Lighthouse audit on https://{domain} using the script at
{skill_path}/scripts/lighthouse.sh
Save the JSON output to /tmp/audit-lighthouse-{domain}.json
Extract and return: performance score, accessibility score,
best-practices score, SEO score, and Core Web Vitals (LCP, FID, CLS).
Perplexity citation check — Spawn a background subagent (skip if no PERPLEXITY_API_KEY):
Agent(perplexity-checker):
model: haiku
prompt: |
Check if {domain} is cited by Perplexity AI using the script at
{skill_path}/scripts/perplexity-check.sh
Domain: {domain}
Generate 5 queries likely to surface this domain based on its content.
Save results to /tmp/audit-perplexity-{domain}.json
Return: number of queries tested, number with citations, citation details.
Homepage crawl — Use Playwright to navigate to https://{domain} and run the extraction function from modules/extraction.js. This captures 20+ signals per page (headings, meta tags, structured data, links, content metrics, etc.).
Phase B: Page Discovery and Classification
Using the sitemap URLs and internal links found on the homepage:
- Build page list. Merge sitemap URLs with discovered internal links. Deduplicate. Cap at 20 pages for audit (prioritise: homepage, key landing pages, blog posts, product pages).
- Classify pages. Assign each URL a type:
homepage, blog, product, about, contact, landing, legal, other. Use URL patterns and page titles to classify.
Phase C: Parallel Page Crawling
For each page in the audit list (beyond the homepage already crawled):
- Navigate with Playwright and run
modules/extraction.js on each page.
- Process pages in batches of 3-5 to avoid overwhelming the browser.
- Store extraction results per URL.
Phase D: Blog/Article Analysis
For pages classified as blog or article:
- Run additional AEO checks: Does the first paragraph answer a question? Are there answer blocks of 40-60 words? Question-based headings?
- Run additional GEO checks: Author visible? Credentials stated? Published date? Sources linked? Fact density?
- These signals feed into the category scoring.
Phase E: Apply Rules and Score
This is where the audit happens. For each requested category:
Read the reference file. Load the relevant file from references/ (e.g., references/aeo.md for AEO).
Apply each rule against the crawled data. For every check, record:
- Check ID (e.g.
AEO-C1)
- Description
- Severity:
Critical, Important, or Nice to have
- Result:
PASS, WARNING, FAIL, N/A, or UNTESTABLE
- Evidence (what you observed)
- Fix (if FAIL or WARNING — what specifically to change)
Also apply references/indexability.md rules (always) and references/ai-bots.md analysis (always, but informational — does not affect score).
Run schema deprecation check if structured-data category is included:
bash {skill_path}/scripts/check-schema-deprecations.sh '<json-ld-content>'
Score deterministically. Write the check results as JSON to a temp file, then run:
python3 {skill_path}/scripts/score.py /tmp/audit-checks-{domain}.json
The scoring engine handles severity weighting, category weighting, and grade calculation. Read its output for the final scores.
Phase F: Generate Report
- Read the report template from
modules/report-template.md.
- Populate the template with all collected data: site profile, AI crawler policy analysis, per-category results with check details, Lighthouse results (when ready), Perplexity citation results (when ready), and the prioritised fix list.
- Write the report with the naming convention:
- Single site:
{YYYY-MM-DD}-audit-{domain}.md
- Comparison:
{YYYY-MM-DD}-compare-{domain1}-vs-{domain2}.md
- Place the report in the current working directory.
Scoring System
The scoring is fully deterministic and handled by scripts/score.py. Here is how it works so you can explain it to the user:
Severity weights: Critical = 3, Important = 2, Nice to have = 1
Result multipliers: PASS = 1.0, WARNING = 0.5, FAIL = 0.0
N/A and UNTESTABLE are excluded from both numerator and denominator — they do not affect the score.
Category score = sum(weight x multiplier) / sum(weights) x 100
Category weights for overall score:
| Category |
Weight |
| AEO |
25% |
| GEO |
25% |
| SEO Technical |
20% |
| SEO On-Page |
15% |
| Structured Data |
15% |
If only some categories are audited, weights redistribute proportionally among audited categories.
Letter grades: A+ (95+), A (90-94), B+ (85-89), B (80-84), C+ (75-79), C (70-74), D (60-69), F (<60)
Check Result JSON Format
When writing check results for the scoring engine, use this structure:
{
"domain": "example.com",
"categories": {
"aeo": {
"checks": [
{
"id": "AEO-C1",
"description": "First paragraph answers the page's primary question",
"severity": "Critical",
"result": "PASS",
"evidence": "First paragraph provides a direct answer in 45 words",
"fix": null
}
]
}
}
}
Comparison Mode
When two domains are provided:
- Run the full audit (Phases A-E) on both domains.
- Instead of two separate reports, produce a single comparison report:
- Side-by-side score table (overall + per category)
- Category-by-category analysis of differences
- Top 3 priority fixes for each site
- Winner per category with reasoning
- Use the comparison naming convention for the output file.
Important Guidance
- Be thorough but honest. Mark checks as
UNTESTABLE when you genuinely cannot verify something from a crawl (e.g., server-side configuration). Do not guess.
- Evidence matters. Every FAIL and WARNING must include specific evidence (the actual meta description length, the actual heading text, the actual schema type found). This makes the report actionable.
- Prioritise fixes. The fix list at the end of the report should be ordered by impact: Critical FAILs first, then Critical WARNINGs, then Important FAILs, and so on.
- Do not fabricate data. If Lighthouse or Perplexity subagents fail or are unavailable, note this in the report and mark those checks as UNTESTABLE. Do not invent scores.
- Respect rate limits. Add 1-2 second delays between Playwright page navigations to avoid being blocked.
1---2name: website-audit3description: Audits any website for SEO, AEO (Answer Engine Optimization), GEO (Generative Engine Optimization), and Structured Data. Crawls with Playwright, runs Lighthouse, checks Perplexity citations, applies research-backed rules, scores deterministically, and produces a prioritised fix list with letter grades. Use this skill whenever the user mentions a domain or URL and wants to know what's wrong with it from an SEO, content, structured data, or AI-readiness perspective — even if they don't use the word 'audit'. Triggers include: auditing a website, checking SEO issues, evaluating AI-readiness or answer-engine optimisation, comparing two sites or documentation portals, reviewing structured data or JSON-LD, checking robots.txt AI crawler policy, running a website health check, assessing whether content will appear in Perplexity/ChatGPT search results, or any request for a scored website quality analysis. Also triggers on: /website-audit [domain] [categories...]4---56# Website Audit78Audit any website across 5 categories: **AEO**, **GEO**, **SEO Technical**, **SEO On-Page**, and **Structured Data**. Produces a scored, prioritised report with actionable fixes.910## Arguments1112```13/website-audit <domain> [categories...] [+refresh] [comparison-domain]14```1516- **domain** (required): The site to audit (e.g. `example.com`)17- **categories** (optional): Subset of `aeo`, `geo`, `seo-technical`, `seo-on-page`, `structured-data`. Omit to audit all 5.18- **+refresh**: Force-refresh reference files even if <30 days old.19- **comparison-domain**: If a second domain is provided, run comparison mode.2021## Dependencies2223Before starting, verify these are available. If any are missing, tell the user what to install and stop.2425- Node.js 22+ (`node --version`)26- Playwright CLI (`npx playwright --version`) — if missing: `npx playwright install chromium`27- Python 3 (`python3 --version`)28- Lighthouse (`lighthouse --version`) — if missing: `npm install -g lighthouse`29- jq (`jq --version`)30- Optional: `PERPLEXITY_API_KEY` env var for citation checking3132---3334## Audit Flow3536Work through these phases in order. Each phase builds on the previous one's data.3738### Phase 0: Setup39401. **Parse arguments.** Extract domain(s) and requested categories. Default to all 5 categories.412. **Check dependencies.** Run the version commands above. Stop on failure.423. **Comparison mode?** If two domains provided, set `COMPARISON_MODE=true`. You will run the full audit on each domain, then produce a comparison report instead of two separate ones.434. **Reference freshness.** Check if any file in `references/` is >30 days old (use file modification time). If so, or if `+refresh` was requested, read `references/refresh-guide.md` and follow its instructions to update stale files. If all files are fresh and no `+refresh`, skip this.4445### Phase A: Crawl Technical Files + Homepage4647Run these four tasks in parallel:48491. **robots.txt + sitemap.xml** — Fetch via curl:50 ```bash51 curl -sL "https://{domain}/robots.txt" -o /tmp/audit-robots.txt52 curl -sL "https://{domain}/sitemap.xml" -o /tmp/audit-sitemap.xml53 ```54 Parse robots.txt for Disallow rules and AI bot directives. Parse sitemap for URL list.55562. **Lighthouse** — Spawn a background subagent:57 ```58 Agent(lighthouse-runner):59 model: haiku60 prompt: |61 Run Lighthouse audit on https://{domain} using the script at62 {skill_path}/scripts/lighthouse.sh63 Save the JSON output to /tmp/audit-lighthouse-{domain}.json64 Extract and return: performance score, accessibility score,65 best-practices score, SEO score, and Core Web Vitals (LCP, FID, CLS).66 ```67683. **Perplexity citation check** — Spawn a background subagent (skip if no `PERPLEXITY_API_KEY`):69 ```70 Agent(perplexity-checker):71 model: haiku72 prompt: |73 Check if {domain} is cited by Perplexity AI using the script at74 {skill_path}/scripts/perplexity-check.sh75 Domain: {domain}76 Generate 5 queries likely to surface this domain based on its content.77 Save results to /tmp/audit-perplexity-{domain}.json78 Return: number of queries tested, number with citations, citation details.79 ```80814. **Homepage crawl** — Use Playwright to navigate to `https://{domain}` and run the extraction function from `modules/extraction.js`. This captures 20+ signals per page (headings, meta tags, structured data, links, content metrics, etc.).8283### Phase B: Page Discovery and Classification8485Using the sitemap URLs and internal links found on the homepage:86871. **Build page list.** Merge sitemap URLs with discovered internal links. Deduplicate. Cap at 20 pages for audit (prioritise: homepage, key landing pages, blog posts, product pages).882. **Classify pages.** Assign each URL a type: `homepage`, `blog`, `product`, `about`, `contact`, `landing`, `legal`, `other`. Use URL patterns and page titles to classify.8990### Phase C: Parallel Page Crawling9192For each page in the audit list (beyond the homepage already crawled):93941. Navigate with Playwright and run `modules/extraction.js` on each page.952. Process pages in batches of 3-5 to avoid overwhelming the browser.963. Store extraction results per URL.9798### Phase D: Blog/Article Analysis99100For pages classified as `blog` or `article`:1011021. Run additional AEO checks: Does the first paragraph answer a question? Are there answer blocks of 40-60 words? Question-based headings?1032. Run additional GEO checks: Author visible? Credentials stated? Published date? Sources linked? Fact density?1043. These signals feed into the category scoring.105106### Phase E: Apply Rules and Score107108This is where the audit happens. For each requested category:1091101. **Read the reference file.** Load the relevant file from `references/` (e.g., `references/aeo.md` for AEO).1112. **Apply each rule** against the crawled data. For every check, record:112 - Check ID (e.g. `AEO-C1`)113 - Description114 - Severity: `Critical`, `Important`, or `Nice to have`115 - Result: `PASS`, `WARNING`, `FAIL`, `N/A`, or `UNTESTABLE`116 - Evidence (what you observed)117 - Fix (if FAIL or WARNING — what specifically to change)1181193. **Also apply** `references/indexability.md` rules (always) and `references/ai-bots.md` analysis (always, but informational — does not affect score).1201214. **Run schema deprecation check** if structured-data category is included:122 ```bash123 bash {skill_path}/scripts/check-schema-deprecations.sh '<json-ld-content>'124 ```1251265. **Score deterministically.** Write the check results as JSON to a temp file, then run:127 ```bash128 python3 {skill_path}/scripts/score.py /tmp/audit-checks-{domain}.json129 ```130 The scoring engine handles severity weighting, category weighting, and grade calculation. Read its output for the final scores.131132### Phase F: Generate Report1331341. **Read the report template** from `modules/report-template.md`.1352. **Populate the template** with all collected data: site profile, AI crawler policy analysis, per-category results with check details, Lighthouse results (when ready), Perplexity citation results (when ready), and the prioritised fix list.1363. **Write the report** with the naming convention:137 - Single site: `{YYYY-MM-DD}-audit-{domain}.md`138 - Comparison: `{YYYY-MM-DD}-compare-{domain1}-vs-{domain2}.md`1394. Place the report in the current working directory.140141---142143## Scoring System144145The scoring is fully deterministic and handled by `scripts/score.py`. Here is how it works so you can explain it to the user:146147**Severity weights:** Critical = 3, Important = 2, Nice to have = 1148149**Result multipliers:** PASS = 1.0, WARNING = 0.5, FAIL = 0.0150151**N/A and UNTESTABLE** are excluded from both numerator and denominator — they do not affect the score.152153**Category score** = sum(weight x multiplier) / sum(weights) x 100154155**Category weights for overall score:**156| Category | Weight |157|----------|--------|158| AEO | 25% |159| GEO | 25% |160| SEO Technical | 20% |161| SEO On-Page | 15% |162| Structured Data | 15% |163164If only some categories are audited, weights redistribute proportionally among audited categories.165166**Letter grades:** A+ (95+), A (90-94), B+ (85-89), B (80-84), C+ (75-79), C (70-74), D (60-69), F (<60)167168---169170## Check Result JSON Format171172When writing check results for the scoring engine, use this structure:173174```json175{176 "domain": "example.com",177 "categories": {178 "aeo": {179 "checks": [180 {181 "id": "AEO-C1",182 "description": "First paragraph answers the page's primary question",183 "severity": "Critical",184 "result": "PASS",185 "evidence": "First paragraph provides a direct answer in 45 words",186 "fix": null187 }188 ]189 }190 }191}192```193194---195196## Comparison Mode197198When two domains are provided:1992001. Run the full audit (Phases A-E) on both domains.2012. Instead of two separate reports, produce a single comparison report:202 - Side-by-side score table (overall + per category)203 - Category-by-category analysis of differences204 - Top 3 priority fixes for each site205 - Winner per category with reasoning2063. Use the comparison naming convention for the output file.207208---209210## Important Guidance211212- **Be thorough but honest.** Mark checks as `UNTESTABLE` when you genuinely cannot verify something from a crawl (e.g., server-side configuration). Do not guess.213- **Evidence matters.** Every FAIL and WARNING must include specific evidence (the actual meta description length, the actual heading text, the actual schema type found). This makes the report actionable.214- **Prioritise fixes.** The fix list at the end of the report should be ordered by impact: Critical FAILs first, then Critical WARNINGs, then Important FAILs, and so on.215- **Do not fabricate data.** If Lighthouse or Perplexity subagents fail or are unavailable, note this in the report and mark those checks as UNTESTABLE. Do not invent scores.216- **Respect rate limits.** Add 1-2 second delays between Playwright page navigations to avoid being blocked.