Measure AI answer visibility, diagnose regressions, and act through Canonry CLI or MCP workflows.
Security
aeo
Audit sites and previews, rank AEO fixes, validate schema, and gate regressions in CI.
What it does
Audit a URL, sitemap, preview deployment, localhost target, or built HTML directory for AEO issues, then return scores, critical defects, coverage, and ranked fixes. It supports changed-page PR reviews, sitemap-origin rewriting, schema checks, llms.txt generation, platform detection, and JSON report comparisons for regression gates.
When to use it
- Pull-request AEO regression review
- Offline audit of built HTML
- Sitemap-wide issue triage
- Schema and llms.txt maintenance
The skill document
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 "" [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 sitefix: apply code changes after an auditschema: validate JSON-LD and entity consistencyllms: create or improvellms.txtandllms-full.txtmonitor: compare changes over time, compare a branch preview against production, or benchmark competitorsdetect-platform: identify the CMS, site builder, framework, or hosting stack a site usescompare: diff two saved--format jsonreports into a regression verdict + exit code (CI gate)
If no mode is provided, default to audit.
Examples
audit https://example.comaudit https://example.com --sitemapaudit https://example.com --sitemap --limit 10audit https://example.com --sitemap --top-issuesaudit https://example.com --sitemap --format agent(slim decision for agents)audit https://example.com --lighthouseaudit https://example.com --require-metaaudit https://example.com --sitemap --require-metaaudit http://localhost:3000 --allow-localaudit http://localhost:3000 --sitemap --rewrite-sitemap-origin --allow-localaudit http://localhost:3000 --sitemap --rewrite-sitemap-origin --allow-local --changed --base main --include-criticalaudit https://staging.example.com --sitemap --rewrite-sitemap-originaudit ./out(static-output mode: audit built HTML offline)audit ./out --base-url https://example.com --require-metafix https://example.comschema https://example.comllms https://example.commonitor https://site-a.com --compare https://site-b.comdetect-platform https://example.comdetect-platform https://example.com --min-confidence highdetect-platform --urls competitors.txtdetect-platform --urls https://a.com,https://b.comcompare --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, ordetect-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 "" [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 ``, 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 "" --sitemap --format json
npx @canonry/aeo-audit@4 "" --sitemap https://example.com/sitemap.xml --format json
npx @canonry/aeo-audit@4 "" --sitemap --limit 10 --format json
npx @canonry/aeo-audit@4 "" --sitemap --top-issues --format json
Flags:
--sitemap [url]— auto-discover the sitemap (tries/sitemap.xml, then/sitemap-index.xml, thenSitemap:directives in/robots.txt) or provide an explicit URL--limit— cap pages audited (default 200, sampled across the site's URL templates rather than taken in sitemap order; `` orders instances within a template)--top-issues— skip per-page output, show only cross-cutting patterns and critical defects--rewrite-sitemap-origin— rewrite every ``'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— git base for--changed(defaultmain)--include-critical— add critical paths to the changed-page set--critical-paths— comma-separated critical paths for--include-critical; defaults to/--require-meta— force exit1if any audited page is missing ``, 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).--lighthouseis 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
count other than one, a missing, a missing meta description) surfaced regardless of how few pages they affect, with the offending pages named (homepage and high sitemap-prioritypages first). These would otherwise be averaged into a passing factor score; the JSON field iscriticalDefectsand critical-severity ones are also promoted to the top ofprioritizedFixes. Shown even with--top-issues. - Cross-cutting issues (factors failing across multiple pages), each with the best-scoring page (
bestScore/bestPageUrl) and astatus:sitewide(a real coverage gap) vs.limited/opportunityfor 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/opportunityfactors demoted below them, scoped to the page(s) that carry them), each costed astemplateCounttemplates overinstanceCountpages - 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
confidenceoffull/representative/indicative. A sample that missed whole templates is labelledindicativeand 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-localonly when the user explicitly wants to audit localhost/private IPs. - Use
--rewrite-sitemap-originwhen a local or staging sitemap emits production canonicals. - Use
--changed --basefor PR work so unrelated site sections do not dominate the result. - Use
--include-critical --critical-paths /,/pricing,/contactwhen important pages should always be checked. - If
--changedfinds 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-pathsor audit explicit URLs separately. - Prefer
--format agentfor agent action,--format jsonfor saved compare baselines, and--format markdownfor 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.prioritizedFixesis a ranked array of objects, each with a stableid,kind, optionalseverity, the completeaffectedPageslist (never truncated),affectsHomepage,prevalencePct, and a humansummary. Cross-cutting fixes also carryavgScore,bestScore/bestPageUrl, and astatus(sitewide|limited|opportunity) — treatlimited/opportunityas 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'scode(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 tohttps://. - The relaxation is scoped to the single host named on the CLI, evaluated per hop. A redirect or sitemap `` 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/.htmfile → single-page report; a directory → aggregated report (--limit,--top-issues,--factors,--include-geo,--include-agent-skills,--require-metaapply). --base-urlmaps files to page URLs (out/about/index.html→/about/; defaulthttps://localhost).index.htmlcollapses to its directory URL; other files drop the.htmlextension.llms.txt,llms-full.txt,robots.txt, andsitemap.xmlare read from the directory root when present.- Partial coverage: server-only signals (redirects,
X-Robots-Tag,Last-Modified,Linkheaders) 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 newseverity:criticaldefect (--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-descriptionisseverity:warning, so it does not trip--fail-on-new-critical; use--require-metaon the audit or--fail-on warningshere. 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-onlyalways exits0(soak mode). - Both reports must be the same mode (two single, or two multi-page). stdout carries only the
CompareReportJSON (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 "" --lighthouse --format json
PAGESPEED_API_KEY=xxx npx @canonry/aeo-audit@4 "" --lighthouse --format json
Constraints:
- Single-URL only — cannot combine with
--sitemapor--detect-platform. Each Lighthouse audit takes 15-30s, which would blow up sitemap runtime. - Optional
PAGESPEED_API_KEYenv var lifts anonymous PSI rate limits (25k/day unauthenticated). - On PSI failure (unreachable target, timeout, HTTP error) the factor scores 0 and surfaces a
timeoutorunreachablefinding 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 "" --detect-platform --format json
npx @canonry/aeo-audit@4 "" --detect-platform --min-confidence high --format json
Flags:
--detect-platform— switch to detection mode instead of auditing--min-confidence— filter tolow(default),medium, orhighconfidence--urls— run on multiple URLs at once (file path, comma-separated list, or-for stdin)--concurrency— 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 "" [flags] --format json - Find factors scoring below 70 (lowest first).
- Apply targeted fixes in the current codebase.
- Prioritize:
- Structured data and schema completeness
llms.txtandllms-full.txtrobots.txtcrawler access- E-E-A-T signals
- FAQ markup
- freshness metadata
- agent-readiness signals: per-page Markdown source endpoints,
robots.txtContent-Signaldirectives (the audit scores the values — setai-input=yes/search=yesto permit AI answers and search indexing;ai-input=noopts 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 "" --sitemap --top-issues --format json --factors structured-data,schema-completeness,schema-validity,entity-consistency
Single page:
npx @canonry/aeo-audit@4 "" --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 `` 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, sameAsFAQPage: mainEntity with at least 3 Q&A pairs (and only oneFAQPageblock 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 "" [flags] --format json --factors ai-access-files - Inspect existing AI-readable files if present.
- Extract key content from the site.
- Generate improved
llms.txtandllms-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 `` when appropriate.
- Expose per-page Markdown source endpoints (a
.mdURL or content negotiation) advertised via `` — 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 jsonreports in the same mode, then run thecomparesubcommand. - 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-localis missing, rerun with--allow-localonly 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.
Questions people ask
- Can it audit a local or private preview site?
- Yes, but only with explicit opt-in via `--allow-local` or `--allow-private`. Access is limited to the single CLI host, while redirects or sitemap entries pointing to other private hosts remain blocked.
- How does it review only pages affected by a pull request?
- Sitemap audits can use `--changed --base <ref>` to select static routes changed since a Git base, with optional critical paths included. Dynamic route templates require known concrete paths or separate explicit URL audits.
- What output can I use in CI?
- Full reports are available as JSON, while agent format returns a compact decision and ranked issues. Saved JSON reports can be diffed with `compare` for a regression verdict and CI exit code, and `--require-meta` can fail when any audited page lacks a meta description.
Related skills
Scores page-level SEO signals and turns the findings into a prioritized P0/P1/P2 repair plan.
Audits one content artifact against 80 CORE-EEAT items and returns evidence gaps, a verdict, and a fix plan.
Turns content into quotable answer blocks and reports GEO scores, query coverage, and citation readiness.
Find and revise AI-writing patterns while preserving unaffected prose and protected content.
Audit, reconcile, and record machine-facing entity identity with evidence and provenance.