AEO
Website: canonry.ai
One skill for audit, preview-branch review, fixes, schema, llms.txt, and monitoring workflows.
Command
Always use the published package:
npx @canonry/aeo-audit@4 "<url>" [flags] --format json
Argument Safety
Never interpolate user input directly into shell commands. Always:
- Validate that the target is either a URL matching
https:// / http:// or a local filesystem path (static-output mode), and that it contains no shell metacharacters.
- Quote every argument individually (e.g.,
npx @canonry/aeo-audit@4 "https://example.com" --format json).
- Pass flags as separate, literal tokens — never construct command strings from raw user text.
- Reject arguments containing characters like
;, |, &, $, `, (, ), {, }, <, >, or newlines.
Modes
audit: score and diagnose a site
fix: apply code changes after an audit
schema: validate JSON-LD and entity consistency
llms: create or improve llms.txt and llms-full.txt
monitor: compare changes over time, compare a branch preview against production, or benchmark competitors
detect-platform: identify the CMS, site builder, framework, or hosting stack a site uses
compare: diff two saved --format json reports into a regression verdict + exit code (CI gate)
If no mode is provided, default to audit.
Examples
audit https://example.com
audit https://example.com --sitemap
audit https://example.com --sitemap --limit 10
audit https://example.com --sitemap --top-issues
audit https://example.com --sitemap --format agent (slim decision for agents)
audit https://example.com --lighthouse
audit https://example.com --require-meta
audit https://example.com --sitemap --require-meta
audit http://localhost:3000 --allow-local
audit http://localhost:3000 --sitemap --rewrite-sitemap-origin --allow-local
audit http://localhost:3000 --sitemap --rewrite-sitemap-origin --allow-local --changed --base main --include-critical
audit https://staging.example.com --sitemap --rewrite-sitemap-origin
audit ./out (static-output mode: audit built HTML offline)
audit ./out --base-url https://example.com --require-meta
fix https://example.com
schema https://example.com
llms https://example.com
monitor https://site-a.com --compare https://site-b.com
detect-platform https://example.com
detect-platform https://example.com --min-confidence high
detect-platform --urls competitors.txt
detect-platform --urls https://a.com,https://b.com
compare --baseline baseline.json --current current.json (fail CI on AEO regression)
Mode Selection
- If the first argument is one of
audit, fix, schema, llms, monitor, or detect-platform, use that mode.
- If no explicit mode is given, infer the intent from the request and default to
audit.
Audit
Use for broad requests such as "audit this site" or "why am I not being cited?"
- Run:
npx @canonry/aeo-audit@4 "<url>" [flags] --format json
- Return:
- Overall score
- Short summary
- Factor breakdown
- Top strengths
- Top fixes
- Metadata such as fetch time and auxiliary file availability
--require-meta (CI gate)
Pass --require-meta (single or sitemap mode) to force exit 1 whenever any audited page is missing <meta name="description">, regardless of the otherwise score-based exit rule. Useful in CI pipelines that need to block deploys on a missing meta description even on otherwise-healthy sites.
Sitemap Mode
Use --sitemap to audit all pages discovered from the site's sitemap:
npx @canonry/aeo-audit@4 "<url>" --sitemap --format json
npx @canonry/aeo-audit@4 "<url>" --sitemap https://example.com/sitemap.xml --format json
npx @canonry/aeo-audit@4 "<url>" --sitemap --limit 10 --format json
npx @canonry/aeo-audit@4 "<url>" --sitemap --top-issues --format json
Flags:
--sitemap [url] — auto-discover the sitemap (tries /sitemap.xml, then /sitemap-index.xml, then Sitemap: directives in /robots.txt) or provide an explicit URL
--limit <n> — cap pages audited (default 200, sampled across the site's URL templates rather than taken in sitemap order; <priority> orders instances within a template)
--top-issues — skip per-page output, show only cross-cutting patterns and critical defects
--rewrite-sitemap-origin — rewrite every <loc>'s origin to the target URL's origin (preserving path/query) before crawling. Use when the sitemap hardcodes the prod/canonical domain but you want to audit a staging host or local dev server.
--changed — filter sitemap URLs to static routes changed since --base; use for PR work
--base <ref> — git base for --changed (default main)
--include-critical — add critical paths to the changed-page set
--critical-paths <list> — comma-separated critical paths for --include-critical; defaults to /
--require-meta — force exit 1 if any audited page is missing <meta name="description">, regardless of overall score (useful as a CI gate)
--include-geo / --include-agent-skills — honored per page in sitemap mode (adds the optional geographic-signals / agent-skill-exposure factors). --lighthouse is not available with --sitemap.
Pages are audited with bounded concurrency (5 in flight) to avoid hammering the target origin.
Returns:
- Per-page scores
- Critical defects — binary, one-line-fix structural defects (an
<h1> count other than one, a missing <title>, a missing meta description) surfaced regardless of how few pages they affect, with the offending pages named (homepage and high sitemap-priority pages first). These would otherwise be averaged into a passing factor score; the JSON field is criticalDefects and critical-severity ones are also promoted to the top of prioritizedFixes. Shown even with --top-issues.
- Cross-cutting issues (factors failing across multiple pages), each with the best-scoring page (
bestScore/bestPageUrl) and a status: sitewide (a real coverage gap) vs. limited/opportunity for page-specific factors (FAQ, definitions) that legitimately apply to only some page types
- Aggregate score
- Prioritized fixes (critical defects first, then site-wide gaps; page-specific
limited/opportunity factors demoted below them, scoped to the page(s) that carry them), each costed as templateCount templates over instanceCount pages
- Templates — pages that share a URL shape and score alike, collapsed into the template that produced them, with the page to fix on. "194 property pages missing schema" is one template edit, not 194
- Coverage — what the aggregate score was taken over: pages audited/discovered and how many URL templates the sample reached, with a
confidence of full / representative / indicative. A sample that missed whole templates is labelled indicative and does not speak for the sections it never saw
Preview / PR Audit Workflow
Use this path for PR review, local production builds, preview deployments, and branch-vs-main questions. Prefer built-in flags over manual sitemap downloads, localtunnel glue, or ad hoc URL scripts.
For a local preview server whose sitemap emits production canonicals:
npx @canonry/aeo-audit@4 "http://localhost:3000" \
--sitemap \
--rewrite-sitemap-origin \
--allow-local \
--changed \
--base main \
--include-critical \
--format agent
Guidance:
- Use
--allow-local only when the user explicitly wants to audit localhost/private IPs.
- Use
--rewrite-sitemap-origin when a local or staging sitemap emits production canonicals.
- Use
--changed --base <ref> for PR work so unrelated site sections do not dominate the result.
- Use
--include-critical --critical-paths /,/pricing,/contact when important pages should always be checked.
- If
--changed finds no static routes, inspect the diff manually. Dynamic route templates cannot be safely converted to concrete URLs without route params; include known concrete paths with --critical-paths or audit explicit URLs separately.
- Prefer
--format agent for agent action, --format json for saved compare baselines, and --format markdown for human summaries.
For branch-vs-production regression review, produce comparable reports first, then run compare:
npx @canonry/aeo-audit@4 "https://production.example" --sitemap --format json > baseline.json
npx @canonry/aeo-audit@4 "http://localhost:3000" --sitemap --rewrite-sitemap-origin --allow-local --format json > current.json
npx @canonry/aeo-audit@4 compare --baseline baseline.json --current current.json --format markdown
Report:
- URLs audited or changed paths selected
- Score/regression verdict from
compare
- Critical defects and prioritized fixes
- Caveats such as local/private opt-in, sitemap origin rewriting, dynamic route templates skipped, or sitemap pages filtered out
Machine-readable output (for agents)
Use --format json for the full report, or --format agent for just the decision: { schemaVersion, tool, mode, url, score, pass, criticalDefectCount, issues }, where issues is the ranked prioritizedFixes and the per-factor/per-page detail is omitted. Prefer --format agent when you only need to decide and act. Key fields for acting on the result without parsing prose:
schemaVersion (on every audit report) versions the JSON shape independently of the package version — pin to it and treat a major bump as breaking; absence means a pre-2.0 report.
prioritizedFixes is a ranked array of objects, each with a stable id, kind, optional severity, the complete affectedPages list (never truncated), affectsHomepage, prevalencePct, and a human summary. Cross-cutting fixes also carry avgScore, bestScore/bestPageUrl, and a status (sitewide | limited | opportunity) — treat limited/opportunity as page-specific tune-ups, not site-wide failures. It's the pre-computed to-do list — no need to re-rank factor scores yourself.
- Stable identifiers everywhere —
criticalDefects[].id, prioritizedFixes[].id, and every factor finding's code (e.g. technical-seo.h1.multiple) — let integrations key on codes rather than message strings.
Auxiliary File Diagnostics
When the audit fetches /llms.txt, /llms-full.txt, /robots.txt, and /sitemap.xml, it probes once with Accept: text/markdown to detect a content-negotiation trap: file responds OK to a bare request but returns a non-2xx response when the client prefers markdown. This catches Astro / Vercel / Starlight setups that 307-redirect .txt → non-existent .md for markdown-accepting clients, making the file invisible to AI content-extraction tools even though the file exists. The diagnostic surfaces as a finding on the AI Access Files (llms.txt, sitemap) factor.
Local Dev / Staging Targets
By default the audit blocks any URL that resolves to a private, loopback, or link-local address (SSRF protection). When the user wants to audit their own dev or staging server, pass --allow-local (alias --allow-private):
npx @canonry/aeo-audit@4 "http://localhost:3000" --allow-local --format json
npx @canonry/aeo-audit@4 "http://10.0.5.20" --allow-private --format json
- Pass the explicit
http:// scheme for local dev servers — a bare host defaults to https://.
- The relaxation is scoped to the single host named on the CLI, evaluated per hop. A redirect or sitemap
<loc> pointing at any other private host (e.g. 169.254.169.254) stays blocked.
- To audit a whole local site whose sitemap hardcodes the prod domain, combine with sitemap origin rewriting:
npx @canonry/aeo-audit@4 "http://localhost:3000" --sitemap --rewrite-sitemap-origin --allow-local --format json
Static-Output Mode
When the user wants to audit built HTML offline (CI on a next export / dist / out directory, or before deploying), pass a filesystem path instead of a URL:
# A directory of built HTML (aggregated like sitemap mode)
npx @canonry/aeo-audit@4 "./out" --base-url https://example.com --format json
# A single built file
npx @canonry/aeo-audit@4 "./dist/index.html" --format json
# Gate CI on missing meta descriptions across the build
npx @canonry/aeo-audit@4 "./out" --require-meta --format json
- A
.html/.htm file → single-page report; a directory → aggregated report (--limit, --top-issues, --factors, --include-geo, --include-agent-skills, --require-meta apply).
--base-url <url> maps files to page URLs (out/about/index.html → <base>/about/; default https://localhost). index.html collapses to its directory URL; other files drop the .html extension.
llms.txt, llms-full.txt, robots.txt, and sitemap.xml are read from the directory root when present.
- Partial coverage: server-only signals (redirects,
X-Robots-Tag, Last-Modified, Link headers) aren't visible from static files. Recommend auditing the deployed URL for full coverage.
Compare / Regression Mode
When the user wants to fail CI on an AEO regression (a PR dropped the score, broke a page, or introduced a structural defect), use the compare subcommand. It diffs two saved --format json reports — a baseline and the current run — and exits non-zero on a regression. It runs no audit and no network; it only reads reports.
# 1. Produce the current report (any mode's --format json output works)
npx @canonry/aeo-audit@4 "./out" --base-url https://example.com --format json > current.json
# 2. Diff against a stored baseline — exit 1 if it regressed
npx @canonry/aeo-audit@4 compare --baseline baseline.json --current current.json
# Write a Markdown summary (for a PR comment) and tighten the overall gate
npx @canonry/aeo-audit@4 compare --baseline baseline.json --current current.json --overall-tolerance 0 --md-out diff.md
# Committed/artifact baselines: hard-fail (exit 2) if factor set / engine major differ
npx @canonry/aeo-audit@4 compare --baseline baseline.json --current current.json --strict-comparability
- A regression is any of: overall/aggregate drop >
--overall-tolerance (default 2); a single page drop > --page-tolerance (default 5); a single factor drop > --factor-tolerance (default 8); a page that was auditing successfully now erroring; a new severity:critical defect (--fail-on-new-critical, default on); or a major report-schema change. Score/page/factor deltas only gate when the two runs are comparable (same factor set, no major engine change) — otherwise they're warnings, not failures.
missing-meta-description is severity:warning, so it does not trip --fail-on-new-critical; use --require-meta on the audit or --fail-on warnings here. Removed pages and new warnings are report-only unless promoted with --fail-on removed-pages,warnings.
- Exit codes:
0 = no regression / improvement / first run (no baseline); 1 = regression; 2 = misconfiguration (mode mismatch, unreadable report, missing --current, or incomparable factor-set/engine under --strict-comparability). --report-only always exits 0 (soak mode).
- Both reports must be the same mode (two single, or two multi-page). stdout carries only the
CompareReport JSON (or Markdown with --format markdown); diagnostics go to stderr.
Lighthouse Mode
Use --lighthouse when the user wants page speed, accessibility, or best-practices scoring alongside the AEO factors. It calls Google PageSpeed Insights (mobile strategy) and aggregates Performance + Accessibility + Best Practices into a single optional factor (weight 8).
npx @canonry/aeo-audit@4 "<url>" --lighthouse --format json
PAGESPEED_API_KEY=xxx npx @canonry/aeo-audit@4 "<url>" --lighthouse --format json
Constraints:
- Single-URL only — cannot combine with
--sitemap or --detect-platform. Each Lighthouse audit takes 15-30s, which would blow up sitemap runtime.
- Optional
PAGESPEED_API_KEY env var lifts anonymous PSI rate limits (25k/day unauthenticated).
- On PSI failure (unreachable target, timeout, HTTP error) the factor scores 0 and surfaces a
timeout or unreachable finding rather than throwing — the rest of the audit still runs.
Detect Platform Mode
Use --detect-platform when the user wants to know what stack a site is built on (e.g., "is this WordPress?", "what framework does competitor X use?", "is this site custom-built?"). This is much faster than a full audit because it skips analyzer scoring.
npx @canonry/aeo-audit@4 "<url>" --detect-platform --format json
npx @canonry/aeo-audit@4 "<url>" --detect-platform --min-confidence high --format json
Flags:
--detect-platform — switch to detection mode instead of auditing
--min-confidence <lvl> — filter to low (default), medium, or high confidence
--urls <src> — run on multiple URLs at once (file path, comma-separated list, or - for stdin)
--concurrency <n> — max in-flight fetches in batch mode (default 5)
The report groups detections by category (CMS, site builder, e-commerce, framework, SSG, hosting), each with a confidence bucket, a 0–100 score, an optional version, and the signals that matched. When the report's isCustom flag is true, no CMS/site-builder/e-commerce platform was identified — the site is likely custom-built. Exit code is 0 when at least one platform is detected, 1 otherwise.
Batch detection
When the user wants to fingerprint many sites at once (competitor lists, customer cohorts), pass --urls:
npx @canonry/aeo-audit@4 --detect-platform --urls urls.txt --format json
npx @canonry/aeo-audit@4 --detect-platform --urls https://a.com,https://b.com --format json
cat urls.txt | npx @canonry/aeo-audit@4 --detect-platform --urls - --format json
The batch report contains a results array; each entry has status: 'success' or 'error', plus the same shape as a single-URL report on success. Per-URL fetch errors do not abort the run. Exit code is 0 when at least one URL succeeded, 1 otherwise.
Fix
Use when the user wants code changes applied after the audit.
- Run:
npx @canonry/aeo-audit@4 "<url>" [flags] --format json
- Find factors scoring below 70 (lowest first).
- Apply targeted fixes in the current codebase.
- Prioritize:
- Structured data and schema completeness
llms.txt and llms-full.txt
robots.txt crawler access
- E-E-A-T signals
- FAQ markup
- freshness metadata
- agent-readiness signals: per-page Markdown source endpoints,
robots.txt Content-Signal directives (the audit scores the values — set ai-input=yes/search=yes to permit AI answers and search indexing; ai-input=no opts out of the real-time AI use AEO depends on), and A2A agent cards (aligned with specification.website)
- Re-run the audit and report the score delta.
Rules:
- Always explain proposed changes and get user confirmation before editing files.
- Do not remove existing schema or content unless the user asks.
- Preserve existing code style and patterns.
- If a fix is ambiguous or high-risk, explain the tradeoff before editing.
Schema
Use when the request is specifically about JSON-LD or schema quality.
Validity issues like duplicate singleton @types and JSON parse errors are per page, so a homepage-only audit misses every subpage. Default to sitemap mode for site-wide schema requests ("audit my schema", "are my FAQ blocks valid?"); use single-URL mode only when the user names one specific page.
Site-wide (default):
npx @canonry/aeo-audit@4 "<url>" --sitemap --top-issues --format json --factors structured-data,schema-completeness,schema-validity,entity-consistency
Single page:
npx @canonry/aeo-audit@4 "<url>" --format json --factors structured-data,schema-completeness,schema-validity,entity-consistency
Report:
- Schema types found
- Property completeness by type
- Missing recommended properties
- Validity errors (duplicate singleton
@types, JSON parse errors, empty <script> blocks) — surface these prominently regardless of overall score; Google drops invalid blocks silently from rich results
- Entity consistency issues
- In sitemap mode: list every affected URL for each validity error so the user can locate per-page duplicates
Provide corrected JSON-LD examples when useful.
Checklist:
LocalBusiness: name, address, telephone, openingHours, priceRange, image, url, geo, areaServed, sameAs
FAQPage: mainEntity with at least 3 Q&A pairs (and only one FAQPage block per page — duplicates invalidate rich results)
HowTo: name and at least 3 steps (singleton — only one per page)
Organization: name, logo, contactPoint, sameAs, foundingDate, url, description
- Singletons that must not repeat per page:
FAQPage, HowTo, Article, BlogPosting, NewsArticle, BreadcrumbList, Product, Recipe
llms.txt
Use when the user wants llms.txt or llms-full.txt created or improved.
If a URL is provided:
- Run:
npx @canonry/aeo-audit@4 "<url>" [flags] --format json --factors ai-access-files
- Inspect existing AI-readable files if present.
- Extract key content from the site.
- Generate improved
llms.txt and llms-full.txt.
If no URL is provided:
- Inspect the current project.
- Extract business name, services, FAQs, contact info, and metadata.
- Generate both files from local sources.
After generation:
- Add
<link rel="alternate" type="text/markdown" href="/llms.txt"> when appropriate.
- Expose per-page Markdown source endpoints (a
.md URL or content negotiation) advertised via <link rel="alternate" type="text/markdown"> — a scored AI-readable signal.
- Suggest adding the files to the sitemap.
Monitor
Use when the user wants progress tracking or a competitor comparison.
Single URL:
- Run the audit.
- Compare against prior results in
.aeo-audit-history/ if present.
- Show overall and per-factor deltas.
- Save the current result.
Comparison mode:
- For branch-vs-production, produce baseline and current
--format json reports in the same mode, then run the compare subcommand.
- For competitor benchmarking, audit both public URLs and show side-by-side factor deltas.
- Highlight advantages, weaknesses, regressions, and priority gaps.
Behavior
- If the task needs a deployed site and no URL is provided, ask for the URL.
- If the task is diagnosis only, do not edit files.
- If the task is a fix request, make edits and verify with a rerun when possible.
- If the URL is unreachable or not HTML, report the exact failure.
- If a local/private URL is requested and
--allow-local is missing, rerun with --allow-local only after confirming local preview auditing is intended.
- If sitemap mode appears to audit production during preview work, rerun with
--rewrite-sitemap-origin.
- Prefer concise, evidence-based recommendations over generic SEO advice.
1---2name: aeo3description: Run AEO audits, preview branch audits, changed-page sitemap audits, local/private preview audits with explicit opt-in, sitemap origin rewriting, static-output audits, regression comparisons, site fixes, schema validation, and llms.txt generation.4---56# AEO78Website: [canonry.ai](https://canonry.ai)910One skill for audit, preview-branch review, fixes, schema, llms.txt, and monitoring workflows.1112## Command1314Always use the published package:1516```bash17npx @canonry/aeo-audit@4 "<url>" [flags] --format json18```1920## Argument Safety2122**Never interpolate user input directly into shell commands.** Always:231. Validate that the target is either a URL matching `https://` / `http://` or a local filesystem path (static-output mode), and that it contains no shell metacharacters.242. Quote every argument individually (e.g., `npx @canonry/aeo-audit@4 "https://example.com" --format json`).253. Pass flags as separate, literal tokens — never construct command strings from raw user text.264. Reject arguments containing characters like `;`, `|`, `&`, `$`, `` ` ``, `(`, `)`, `{`, `}`, `<`, `>`, or newlines.2728## Modes2930- `audit`: score and diagnose a site31- `fix`: apply code changes after an audit32- `schema`: validate JSON-LD and entity consistency33- `llms`: create or improve `llms.txt` and `llms-full.txt`34- `monitor`: compare changes over time, compare a branch preview against production, or benchmark competitors35- `detect-platform`: identify the CMS, site builder, framework, or hosting stack a site uses36- `compare`: diff two saved `--format json` reports into a regression verdict + exit code (CI gate)3738If no mode is provided, default to `audit`.3940## Examples4142- `audit https://example.com`43- `audit https://example.com --sitemap`44- `audit https://example.com --sitemap --limit 10`45- `audit https://example.com --sitemap --top-issues`46- `audit https://example.com --sitemap --format agent` (slim decision for agents)47- `audit https://example.com --lighthouse`48- `audit https://example.com --require-meta`49- `audit https://example.com --sitemap --require-meta`50- `audit http://localhost:3000 --allow-local`51- `audit http://localhost:3000 --sitemap --rewrite-sitemap-origin --allow-local`52- `audit http://localhost:3000 --sitemap --rewrite-sitemap-origin --allow-local --changed --base main --include-critical`53- `audit https://staging.example.com --sitemap --rewrite-sitemap-origin`54- `audit ./out` (static-output mode: audit built HTML offline)55- `audit ./out --base-url https://example.com --require-meta`56- `fix https://example.com`57- `schema https://example.com`58- `llms https://example.com`59- `monitor https://site-a.com --compare https://site-b.com`60- `detect-platform https://example.com`61- `detect-platform https://example.com --min-confidence high`62- `detect-platform --urls competitors.txt`63- `detect-platform --urls https://a.com,https://b.com`64- `compare --baseline baseline.json --current current.json` (fail CI on AEO regression)6566## Mode Selection6768- If the first argument is one of `audit`, `fix`, `schema`, `llms`, `monitor`, or `detect-platform`, use that mode.69- If no explicit mode is given, infer the intent from the request and default to `audit`.7071## Audit7273Use for broad requests such as "audit this site" or "why am I not being cited?"74751. Run:76 ```bash77 npx @canonry/aeo-audit@4 "<url>" [flags] --format json78 ```792. Return:80 - Overall score81 - Short summary82 - Factor breakdown83 - Top strengths84 - Top fixes85 - Metadata such as fetch time and auxiliary file availability8687#### `--require-meta` (CI gate)8889Pass `--require-meta` (single or sitemap mode) to force exit `1` whenever any audited page is missing `<meta name="description">`, regardless of the otherwise score-based exit rule. Useful in CI pipelines that need to block deploys on a missing meta description even on otherwise-healthy sites.9091### Sitemap Mode9293Use `--sitemap` to audit all pages discovered from the site's sitemap:9495```bash96npx @canonry/aeo-audit@4 "<url>" --sitemap --format json97npx @canonry/aeo-audit@4 "<url>" --sitemap https://example.com/sitemap.xml --format json98npx @canonry/aeo-audit@4 "<url>" --sitemap --limit 10 --format json99npx @canonry/aeo-audit@4 "<url>" --sitemap --top-issues --format json100```101102Flags:103- `--sitemap [url]` — auto-discover the sitemap (tries `/sitemap.xml`, then `/sitemap-index.xml`, then `Sitemap:` directives in `/robots.txt`) or provide an explicit URL104- `--limit <n>` — cap pages audited (default 200, sampled across the site's URL templates rather than taken in sitemap order; `<priority>` orders instances within a template)105- `--top-issues` — skip per-page output, show only cross-cutting patterns and critical defects106- `--rewrite-sitemap-origin` — rewrite every `<loc>`'s origin to the target URL's origin (preserving path/query) before crawling. Use when the sitemap hardcodes the prod/canonical domain but you want to audit a staging host or local dev server.107- `--changed` — filter sitemap URLs to static routes changed since `--base`; use for PR work108- `--base <ref>` — git base for `--changed` (default `main`)109- `--include-critical` — add critical paths to the changed-page set110- `--critical-paths <list>` — comma-separated critical paths for `--include-critical`; defaults to `/`111- `--require-meta` — force exit `1` if any audited page is missing `<meta name="description">`, regardless of overall score (useful as a CI gate)112- `--include-geo` / `--include-agent-skills` — honored per page in sitemap mode (adds the optional geographic-signals / agent-skill-exposure factors). `--lighthouse` is not available with `--sitemap`.113114Pages are audited with bounded concurrency (5 in flight) to avoid hammering the target origin.115116Returns:117- Per-page scores118- **Critical defects** — binary, one-line-fix structural defects (an `<h1>` count other than one, a missing `<title>`, a missing meta description) surfaced **regardless of how few pages they affect**, with the offending pages named (homepage and high sitemap-`priority` pages first). These would otherwise be averaged into a passing factor score; the JSON field is `criticalDefects` and critical-severity ones are also promoted to the top of `prioritizedFixes`. Shown even with `--top-issues`.119- Cross-cutting issues (factors failing across multiple pages), each with the best-scoring page (`bestScore`/`bestPageUrl`) and a `status`: `sitewide` (a real coverage gap) vs. `limited`/`opportunity` for page-specific factors (FAQ, definitions) that legitimately apply to only some page types120- Aggregate score121- Prioritized fixes (critical defects first, then site-wide gaps; page-specific `limited`/`opportunity` factors demoted below them, scoped to the page(s) that carry them), each costed as `templateCount` templates over `instanceCount` pages122- **Templates** — pages that share a URL shape *and* score alike, collapsed into the template that produced them, with the page to fix on. "194 property pages missing schema" is one template edit, not 194123- **Coverage** — what the aggregate score was taken over: pages audited/discovered and how many URL templates the sample reached, with a `confidence` of `full` / `representative` / `indicative`. A sample that missed whole templates is labelled `indicative` and does not speak for the sections it never saw124125### Preview / PR Audit Workflow126127Use this path for PR review, local production builds, preview deployments, and branch-vs-main questions. Prefer built-in flags over manual sitemap downloads, localtunnel glue, or ad hoc URL scripts.128129For a local preview server whose sitemap emits production canonicals:130131```bash132npx @canonry/aeo-audit@4 "http://localhost:3000" \133 --sitemap \134 --rewrite-sitemap-origin \135 --allow-local \136 --changed \137 --base main \138 --include-critical \139 --format agent140```141142Guidance:143- Use `--allow-local` only when the user explicitly wants to audit localhost/private IPs.144- Use `--rewrite-sitemap-origin` when a local or staging sitemap emits production canonicals.145- Use `--changed --base <ref>` for PR work so unrelated site sections do not dominate the result.146- Use `--include-critical --critical-paths /,/pricing,/contact` when important pages should always be checked.147- If `--changed` finds no static routes, inspect the diff manually. Dynamic route templates cannot be safely converted to concrete URLs without route params; include known concrete paths with `--critical-paths` or audit explicit URLs separately.148- Prefer `--format agent` for agent action, `--format json` for saved compare baselines, and `--format markdown` for human summaries.149150For branch-vs-production regression review, produce comparable reports first, then run `compare`:151152```bash153npx @canonry/aeo-audit@4 "https://production.example" --sitemap --format json > baseline.json154npx @canonry/aeo-audit@4 "http://localhost:3000" --sitemap --rewrite-sitemap-origin --allow-local --format json > current.json155npx @canonry/aeo-audit@4 compare --baseline baseline.json --current current.json --format markdown156```157158Report:159- URLs audited or changed paths selected160- Score/regression verdict from `compare`161- Critical defects and prioritized fixes162- Caveats such as local/private opt-in, sitemap origin rewriting, dynamic route templates skipped, or sitemap pages filtered out163164#### Machine-readable output (for agents)165166Use `--format json` for the full report, or **`--format agent`** for just the decision: `{ schemaVersion, tool, mode, url, score, pass, criticalDefectCount, issues }`, where `issues` is the ranked `prioritizedFixes` and the per-factor/per-page detail is omitted. Prefer `--format agent` when you only need to decide and act. Key fields for acting on the result without parsing prose:167- `schemaVersion` (on every audit report) versions the JSON shape independently of the package version — pin to it and treat a major bump as breaking; absence means a pre-2.0 report.168- `prioritizedFixes` is a ranked array of objects, each with a stable `id`, `kind`, optional `severity`, the complete `affectedPages` list (never truncated), `affectsHomepage`, `prevalencePct`, and a human `summary`. Cross-cutting fixes also carry `avgScore`, `bestScore`/`bestPageUrl`, and a `status` (`sitewide` | `limited` | `opportunity`) — treat `limited`/`opportunity` as page-specific tune-ups, not site-wide failures. It's the pre-computed to-do list — no need to re-rank factor scores yourself.169- Stable identifiers everywhere — `criticalDefects[].id`, `prioritizedFixes[].id`, and every factor finding's `code` (e.g. `technical-seo.h1.multiple`) — let integrations key on codes rather than message strings.170171#### Auxiliary File Diagnostics172173When the audit fetches `/llms.txt`, `/llms-full.txt`, `/robots.txt`, and `/sitemap.xml`, it probes once with `Accept: text/markdown` to detect a **content-negotiation** trap: file responds OK to a bare request but returns a non-2xx response when the client prefers markdown. This catches Astro / Vercel / Starlight setups that 307-redirect `.txt` → non-existent `.md` for markdown-accepting clients, making the file invisible to AI content-extraction tools even though the file exists. The diagnostic surfaces as a finding on the **AI Access Files (llms.txt, sitemap)** factor.174175### Local Dev / Staging Targets176177By default the audit blocks any URL that resolves to a private, loopback, or link-local address (SSRF protection). When the user wants to audit **their own** dev or staging server, pass `--allow-local` (alias `--allow-private`):178179```bash180npx @canonry/aeo-audit@4 "http://localhost:3000" --allow-local --format json181npx @canonry/aeo-audit@4 "http://10.0.5.20" --allow-private --format json182```183184- Pass the explicit `http://` scheme for local dev servers — a bare host defaults to `https://`.185- The relaxation is scoped to the **single host named on the CLI**, evaluated per hop. A redirect or sitemap `<loc>` pointing at any other private host (e.g. `169.254.169.254`) stays blocked.186- To audit a whole local site whose sitemap hardcodes the prod domain, combine with sitemap origin rewriting:187188```bash189npx @canonry/aeo-audit@4 "http://localhost:3000" --sitemap --rewrite-sitemap-origin --allow-local --format json190```191192### Static-Output Mode193194When the user wants to audit **built HTML offline** (CI on a `next export` / `dist` / `out` directory, or before deploying), pass a filesystem path instead of a URL:195196```bash197# A directory of built HTML (aggregated like sitemap mode)198npx @canonry/aeo-audit@4 "./out" --base-url https://example.com --format json199# A single built file200npx @canonry/aeo-audit@4 "./dist/index.html" --format json201# Gate CI on missing meta descriptions across the build202npx @canonry/aeo-audit@4 "./out" --require-meta --format json203```204205- A `.html`/`.htm` file → single-page report; a directory → aggregated report (`--limit`, `--top-issues`, `--factors`, `--include-geo`, `--include-agent-skills`, `--require-meta` apply).206- `--base-url <url>` maps files to page URLs (`out/about/index.html` → `<base>/about/`; default `https://localhost`). `index.html` collapses to its directory URL; other files drop the `.html` extension.207- `llms.txt`, `llms-full.txt`, `robots.txt`, and `sitemap.xml` are read from the directory root when present.208- **Partial coverage:** server-only signals (redirects, `X-Robots-Tag`, `Last-Modified`, `Link` headers) aren't visible from static files. Recommend auditing the deployed URL for full coverage.209210### Compare / Regression Mode211212When the user wants to **fail CI on an AEO regression** (a PR dropped the score, broke a page, or introduced a structural defect), use the `compare` subcommand. It diffs two saved `--format json` reports — a baseline and the current run — and exits non-zero on a regression. It runs no audit and no network; it only reads reports.213214```bash215# 1. Produce the current report (any mode's --format json output works)216npx @canonry/aeo-audit@4 "./out" --base-url https://example.com --format json > current.json217# 2. Diff against a stored baseline — exit 1 if it regressed218npx @canonry/aeo-audit@4 compare --baseline baseline.json --current current.json219# Write a Markdown summary (for a PR comment) and tighten the overall gate220npx @canonry/aeo-audit@4 compare --baseline baseline.json --current current.json --overall-tolerance 0 --md-out diff.md221# Committed/artifact baselines: hard-fail (exit 2) if factor set / engine major differ222npx @canonry/aeo-audit@4 compare --baseline baseline.json --current current.json --strict-comparability223```224225- **A regression is any of:** overall/aggregate drop > `--overall-tolerance` (default 2); a single page drop > `--page-tolerance` (default 5); a single factor drop > `--factor-tolerance` (default 8); a page that was auditing successfully now erroring; a new `severity:critical` defect (`--fail-on-new-critical`, default on); or a major report-schema change. Score/page/factor deltas only gate when the two runs are **comparable** (same factor set, no major engine change) — otherwise they're warnings, not failures.226- `missing-meta-description` is `severity:warning`, so it does **not** trip `--fail-on-new-critical`; use `--require-meta` on the audit or `--fail-on warnings` here. Removed pages and new warnings are report-only unless promoted with `--fail-on removed-pages,warnings`.227- **Exit codes:** `0` = no regression / improvement / first run (no baseline); `1` = regression; `2` = misconfiguration (mode mismatch, unreadable report, missing `--current`, or incomparable factor-set/engine under `--strict-comparability`). `--report-only` always exits `0` (soak mode).228- Both reports must be the same mode (two single, or two multi-page). stdout carries only the `CompareReport` JSON (or Markdown with `--format markdown`); diagnostics go to stderr.229230### Lighthouse Mode231232Use `--lighthouse` when the user wants page speed, accessibility, or best-practices scoring alongside the AEO factors. It calls Google PageSpeed Insights (mobile strategy) and aggregates Performance + Accessibility + Best Practices into a single optional factor (weight 8).233234```bash235npx @canonry/aeo-audit@4 "<url>" --lighthouse --format json236PAGESPEED_API_KEY=xxx npx @canonry/aeo-audit@4 "<url>" --lighthouse --format json237```238239Constraints:240- Single-URL only — cannot combine with `--sitemap` or `--detect-platform`. Each Lighthouse audit takes 15-30s, which would blow up sitemap runtime.241- Optional `PAGESPEED_API_KEY` env var lifts anonymous PSI rate limits (25k/day unauthenticated).242- On PSI failure (unreachable target, timeout, HTTP error) the factor scores 0 and surfaces a `timeout` or `unreachable` finding rather than throwing — the rest of the audit still runs.243244### Detect Platform Mode245246Use `--detect-platform` when the user wants to know what stack a site is built on (e.g., "is this WordPress?", "what framework does competitor X use?", "is this site custom-built?"). This is much faster than a full audit because it skips analyzer scoring.247248```bash249npx @canonry/aeo-audit@4 "<url>" --detect-platform --format json250npx @canonry/aeo-audit@4 "<url>" --detect-platform --min-confidence high --format json251```252253Flags:254- `--detect-platform` — switch to detection mode instead of auditing255- `--min-confidence <lvl>` — filter to `low` (default), `medium`, or `high` confidence256- `--urls <src>` — run on multiple URLs at once (file path, comma-separated list, or `-` for stdin)257- `--concurrency <n>` — max in-flight fetches in batch mode (default 5)258259The report groups detections by category (CMS, site builder, e-commerce, framework, SSG, hosting), each with a confidence bucket, a 0–100 score, an optional version, and the signals that matched. When the report's `isCustom` flag is true, no CMS/site-builder/e-commerce platform was identified — the site is likely custom-built. Exit code is `0` when at least one platform is detected, `1` otherwise.260261#### Batch detection262263When the user wants to fingerprint many sites at once (competitor lists, customer cohorts), pass `--urls`:264265```bash266npx @canonry/aeo-audit@4 --detect-platform --urls urls.txt --format json267npx @canonry/aeo-audit@4 --detect-platform --urls https://a.com,https://b.com --format json268cat urls.txt | npx @canonry/aeo-audit@4 --detect-platform --urls - --format json269```270271The batch report contains a `results` array; each entry has `status: 'success'` or `'error'`, plus the same shape as a single-URL report on success. Per-URL fetch errors do not abort the run. Exit code is `0` when at least one URL succeeded, `1` otherwise.272273## Fix274275Use when the user wants code changes applied after the audit.2762771. Run:278 ```bash279 npx @canonry/aeo-audit@4 "<url>" [flags] --format json280 ```2812. Find factors scoring below 70 (lowest first).2823. Apply targeted fixes in the current codebase.2834. Prioritize:284 - Structured data and schema completeness285 - `llms.txt` and `llms-full.txt`286 - `robots.txt` crawler access287 - E-E-A-T signals288 - FAQ markup289 - freshness metadata290 - agent-readiness signals: per-page Markdown source endpoints, `robots.txt` `Content-Signal` directives (the audit scores the values — set `ai-input=yes`/`search=yes` to permit AI answers and search indexing; `ai-input=no` opts out of the real-time AI use AEO depends on), and A2A agent cards (aligned with specification.website)2915. Re-run the audit and report the score delta.292293Rules:294- Always explain proposed changes and get user confirmation before editing files.295- Do not remove existing schema or content unless the user asks.296- Preserve existing code style and patterns.297- If a fix is ambiguous or high-risk, explain the tradeoff before editing.298299## Schema300301Use when the request is specifically about JSON-LD or schema quality.302303Validity issues like duplicate singleton `@type`s and JSON parse errors are **per page**, so a homepage-only audit misses every subpage. Default to sitemap mode for site-wide schema requests ("audit my schema", "are my FAQ blocks valid?"); use single-URL mode only when the user names one specific page.304305Site-wide (default):306307```bash308npx @canonry/aeo-audit@4 "<url>" --sitemap --top-issues --format json --factors structured-data,schema-completeness,schema-validity,entity-consistency309```310311Single page:312313```bash314npx @canonry/aeo-audit@4 "<url>" --format json --factors structured-data,schema-completeness,schema-validity,entity-consistency315```316317Report:318- Schema types found319- Property completeness by type320- Missing recommended properties321- **Validity errors** (duplicate singleton `@type`s, JSON parse errors, empty `<script>` blocks) — surface these prominently regardless of overall score; Google drops invalid blocks silently from rich results322- Entity consistency issues323- In sitemap mode: list every affected URL for each validity error so the user can locate per-page duplicates324325Provide corrected JSON-LD examples when useful.326327Checklist:328- `LocalBusiness`: name, address, telephone, openingHours, priceRange, image, url, geo, areaServed, sameAs329- `FAQPage`: mainEntity with at least 3 Q&A pairs (and only **one** `FAQPage` block per page — duplicates invalidate rich results)330- `HowTo`: name and at least 3 steps (singleton — only one per page)331- `Organization`: name, logo, contactPoint, sameAs, foundingDate, url, description332- Singletons that must not repeat per page: `FAQPage`, `HowTo`, `Article`, `BlogPosting`, `NewsArticle`, `BreadcrumbList`, `Product`, `Recipe`333334## llms.txt335336Use when the user wants `llms.txt` or `llms-full.txt` created or improved.337338If a URL is provided:3391. Run:340 ```bash341 npx @canonry/aeo-audit@4 "<url>" [flags] --format json --factors ai-access-files342 ```3432. Inspect existing AI-readable files if present.3443. Extract key content from the site.3454. Generate improved `llms.txt` and `llms-full.txt`.346347If no URL is provided:3481. Inspect the current project.3492. Extract business name, services, FAQs, contact info, and metadata.3503. Generate both files from local sources.351352After generation:353- Add `<link rel="alternate" type="text/markdown" href="/llms.txt">` when appropriate.354- Expose per-page Markdown source endpoints (a `.md` URL or content negotiation) advertised via `<link rel="alternate" type="text/markdown">` — a scored AI-readable signal.355- Suggest adding the files to the sitemap.356357## Monitor358359Use when the user wants progress tracking or a competitor comparison.360361Single URL:3621. Run the audit.3632. Compare against prior results in `.aeo-audit-history/` if present.3643. Show overall and per-factor deltas.3654. Save the current result.366367Comparison mode:3681. For branch-vs-production, produce baseline and current `--format json` reports in the same mode, then run the `compare` subcommand.3692. For competitor benchmarking, audit both public URLs and show side-by-side factor deltas.3703. Highlight advantages, weaknesses, regressions, and priority gaps.371372## Behavior373374- If the task needs a deployed site and no URL is provided, ask for the URL.375- If the task is diagnosis only, do not edit files.376- If the task is a fix request, make edits and verify with a rerun when possible.377- If the URL is unreachable or not HTML, report the exact failure.378- If a local/private URL is requested and `--allow-local` is missing, rerun with `--allow-local` only after confirming local preview auditing is intended.379- If sitemap mode appears to audit production during preview work, rerun with `--rewrite-sitemap-origin`.380- Prefer concise, evidence-based recommendations over generic SEO advice.