Not in the top 1,000 by GitHub stars
Hainrixz/claude-seo-ai
The SEO + AI-search (GEO/AEO) optimization toolkit for Claude Code — two-score audit + opt-in fixer. Built for 2026-2027.
History
Review weekly changes to SKILL.md before updating. Dates mark the weeks in which we observed each version; checks apply to the recorded commits.
View weekly changes
skills/audit/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/compare/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/fix/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/geo/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/score/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-agent-readiness/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-ai-crawlers/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-ai-discovery/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-core-web-vitals/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-crawl-render/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-crawlability/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-ecommerce/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-eeat/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-entity-linking/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-fix-apply/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Instructions only
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-freshness/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-geo-answerblocks/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-geo-factdensity/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-headings-structure/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-images-media/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-indexability/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-internal-linking/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-international/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-local/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-meta-onpage/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-mobile/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-orchestrator/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-platform-detect/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-rendering/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-schema-jsonld/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-score/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-sitemaps/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-social-cards/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Network or broad shell
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
skills/seo-vertical-detect/SKILL.md
Week of Oct 26, 2026 · Line counts unavailable
Before Not checked yet → After Instructions only
First observation → 0a7ef08986ea
No earlier version was observed for comparison.
Verified commit 0a7ef08986eabfa6452f7055cddb6582d074b9d7
git clone --filter=blob:none --no-checkout https://github.com/Hainrixz/claude-seo-ai.git .hotskills/hainrixz-claude-seo-ai && git -C .hotskills/hainrixz-claude-seo-ai checkout --detach 0a7ef08986eabfa6452f7055cddb6582d074b9d7
# Copy each selected skill directory from .hotskills/hainrixz-claude-seo-ai into .claude/skills/<skill-name>/Heat
5.6- Star growth, last 7 daysNot available
- Star growth, last 24 hoursNot available
- Rank movement, last 7 daysNot available
- Commits, last 30 daysNot available
- Accounts asking about it through MCP, last 7 daysNot available
- Hot votes this weekNot available
Heat never changes the order by stars, and sponsorship never touches it. How heat works
Stars over time
30-day change not yet available: not enough history.
Individual skills in this repository
- audit
skills/audit - compare
skills/compare - fix
skills/fix - geo
skills/geo - score
skills/score - seo-agent-readiness
skills/seo-agent-readiness - seo-ai-crawlers
skills/seo-ai-crawlers - seo-ai-discovery
skills/seo-ai-discovery - seo-core-web-vitals
skills/seo-core-web-vitals - seo-crawl-render
skills/seo-crawl-render - seo-crawlability
skills/seo-crawlability - seo-ecommerce
skills/seo-ecommerce - seo-eeat
skills/seo-eeat - seo-entity-linking
skills/seo-entity-linking - seo-fix-apply
skills/seo-fix-apply - seo-freshness
skills/seo-freshness - seo-geo-answerblocks
skills/seo-geo-answerblocks - seo-geo-factdensity
skills/seo-geo-factdensity - seo-headings-structure
skills/seo-headings-structure - seo-images-media
skills/seo-images-media - seo-indexability
skills/seo-indexability - seo-internal-linking
skills/seo-internal-linking - seo-international
skills/seo-international - seo-local
skills/seo-local - seo-meta-onpage
skills/seo-meta-onpage - seo-mobile
skills/seo-mobile - seo-orchestrator
skills/seo-orchestrator - seo-platform-detect
skills/seo-platform-detect - seo-rendering
skills/seo-rendering - seo-schema-jsonld
skills/seo-schema-jsonld - seo-score
skills/seo-score - seo-sitemaps
skills/seo-sitemaps - seo-social-cards
skills/seo-social-cards - seo-vertical-detect
skills/seo-vertical-detect
How it works
audit
When your agent uses it
Audit a website or web codebase for SEO and AI-search (GEO/AEO) — produces two independent 0-100 scores (Search SEO + AI Visibility) plus a prioritized, evidence-backed report persisted on disk. Read-only; never writes to the project. Use when the user asks to audit, analyze, check, or score a site's SEO, structured data/schema, meta tags, Core Web Vitals, robots.txt, sitemaps, or AI discovery files, or how it ranks in Google and is cited by AI engines (ChatGPT, Perplexity, Google AI Overviews, Gemini, Claude).
What it loads into context
Frontmatter
232 tokensAlways available to the agent
SKILL.md
952 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- What to do
Files
../../.claude-plugin/marketplace.jsonasset695 bytes../../.claude-plugin/plugin.jsonasset4,772 bytes../../hooks/hooks.jsonasset610 bytes../../hooks/hooks.jsonasset610 bytes../../hooks/hooks.jsonasset610 bytesSKILL.mdcore3,653 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# /claude-seo-ai:audit
A full, **read-only** SEO + AI-search audit. Never writes to the user's project; artifacts go to `${CLAUDE_PLUGIN_DATA}/runs` (or `--out`).
`$ARGUMENTS` = `<url|path> [flags]`. The target is a URL (`https://…`) or a local web project / built HTML. If no target is given, ask for one. Flags are forwarded verbatim to the orchestrator, which passes them to `audit.mjs`: `--pages N` (pages to sample, default 12) · `--max N` (URLs to consider, default 40) · `--render static|auto|js` (**default `static`** — a headless run never launches a browser unless asked) · `--ua default|googlebot|bingbot|gptbot|oai-searchbot|claude-searchbot` (or a literal UA string) · `--vertical <ids>` (comma list of `saas,blog-publisher,local-business,ecommerce,docs,generic`; omit it to let the run infer one) · `--environment production|preview|staging|local` · `--feed <path>` (a product feed for the ACP lint) · `--out <dir>` (runs root; defaults to `${CLAUDE_PLUGIN_DATA}/runs`).
## What to do
1. Invoke the **seo-orchestrator** skill with the target and all flags. It runs `audit.mjs` once — acquisition (crawl/snapshot → PageSnapshot v2), platform profile, vertical guess and the whole deterministic checks registry in a single call — refines the vertical with **seo-vertical-detect** (`references/routing.md`), dispatches the read-only specialist subagents in parallel with a dispatch envelope, then merges everything back with `report.mjs --merge` and scores.
2. Present, from `report.json`:
- The two scores — **Search SEO** and **AI Visibility** — each with a band (A–F) and a one-line interpretation. Never blended.
- A per-category breakdown for each score; **coverage mode** (full vs deterministic-only), the data **tier** reached, and the `needs_api` / `manual_review` counts (score confidence).
- The **sampling table**: pages crawled by role, templates discovered vs sampled, and every skip with its reason.
- The **platform profile** (platform/framework/plugins, environment — e.g. "noindex expected on preview").View the complete source on GitHubcompare
When your agent uses it
Compare a site against its own baseline, against a staging deployment, against up to four competitors, or against the pages that already answer a query (content gap). Produces score, category, finding and structure deltas from persisted audit runs. Read-only. Use when the user asks what changed after a deploy, whether staging is worse than production, how they stack up against competitors, or what rivals cover that they do not.
What it loads into context
Frontmatter
168 tokensAlways available to the agent
SKILL.md
1,993 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Flow A — baseline, staging, competitor
- Flow B — content gap
- Honesty
Scripts executed separately
../../scripts/audit.mjs../../scripts/audit.mjs../../scripts/audit.mjs../../scripts/audit.mjs../../scripts/compare.mjs../../scripts/compare.mjs../../scripts/score.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/audit.mjsruns23,375 bytes../../scripts/audit.mjsruns23,375 bytes../../scripts/audit.mjsruns23,375 bytes../../scripts/audit.mjsruns23,375 bytes../../scripts/compare.mjsruns83,837 bytes../../scripts/compare.mjsruns83,837 bytes../../scripts/score.mjsruns43,922 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore7,812 bytesSKILL.mdcore7,812 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# /claude-seo-ai:compare
Four comparisons, one script. **Read-only**: everything is written under `${CLAUDE_PLUGIN_DATA}/runs` (comparisons land in `<root>/compare/<a>__<b>/compare.json`), never in the user's project.
`$ARGUMENTS` decides the shape:
| The user gave you | Run |
| --- | --- |
| two URLs / run refs | `compare.mjs <refA> <refB>` — same host is a **baseline**, a staging-looking host is **staging**, two sites are **competitor** |
| three to five refs | `compare.mjs <refA> <refB> <refC> …` — competitor matrix (max 5) |
| "since my last audit" | `compare.mjs --baseline latest --against <url\|run>` |
| staging vs production | `compare.mjs --prod <ref> --staging <ref>` (add `--map staging.example.com=example.com` when the hosts do not look related) |
| "what do the pages ranking for X have that I don't" | the content-gap flow below |View the complete source on GitHubfix
When your agent uses it
Opt-in fixer (the /claude-seo-ai:fix command). Applies the safe, deterministic SEO/AI-search fixes from a persisted audit to the user's site — meta viewport/charset/lang, JSON-LD, robots.txt AI directives, hreflang, sitemaps, OG/Twitter cards, image dimensions, canonical, AI discovery files — through the adapter that matches the platform (local files, Shopify theme/Admin, WordPress REST/WP-CLI, page APIs, or step-by-step instructions). Dry-run preview by default; writes only after explicit per-change confirmation. Runs only when the user invokes it — never auto-triggered.
What it loads into context
Frontmatter
329 tokensAlways available to the agent
SKILL.md
3,089 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Fixability classes (from each finding's `fixable` field — see `schema/finding.schema.json`)
- Setup for every step
- Flow
- Target shapes
- Safety (hard rules)
Scripts executed separately
../../scripts/fix-plan.mjs../../scripts/fix-plan.mjs../../scripts/fix-plan.mjs../../scripts/fix-plan.mjs../../scripts/fix-plan.mjs../../scripts/fix-plan.mjs../../scripts/fix-plan.mjs../../scripts/fix-plan.mjs../../scripts/fix-ticket.mjs../../scripts/fix-ticket.mjs../../scripts/fix-ticket.mjs../../scripts/guard-bash.mjs
Files
../../scripts/fix-plan.mjsruns33,309 bytes../../scripts/fix-plan.mjsruns33,309 bytes../../scripts/fix-plan.mjsruns33,309 bytes../../scripts/fix-plan.mjsruns33,309 bytes../../scripts/fix-plan.mjsruns33,309 bytes../../scripts/fix-plan.mjsruns33,309 bytes../../scripts/fix-plan.mjsruns33,309 bytes../../scripts/fix-plan.mjsruns33,309 bytes../../scripts/fix-ticket.mjsruns6,731 bytes../../scripts/fix-ticket.mjsruns6,731 bytes../../scripts/fix-ticket.mjsruns6,731 bytes../../scripts/guard-bash.mjsruns15,991 bytesSKILL.mdcore11,844 bytesSKILL.mdcore11,844 bytesSKILL.mdcore11,844 bytesSKILL.mdcore11,844 bytesSKILL.mdcore11,844 bytesSKILL.mdcore11,844 bytesSKILL.mdcore11,844 bytesSKILL.mdcore11,844 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# fix (opt-in writer)
`disable-model-invocation: true` means the model can **never** trigger this on its own — only the
user running `/claude-seo-ai:fix`. Writes happen only through the **seo-fixer-writer** subagent (the
one agent with Write/Edit) and only after explicit confirmation.
`--category` filters the report's findings and matches on a finding's **module id** (`M5`, `M7`, `M17`, …),
its **axis** (`search`, `ai`, or `both` — which matches both of the other two), its **fixable class**
(`auto`, `proposed`, `advisory`), or its **scope** (`page`, `site`, …), case-insensitively. It is **not**
a topic vocabulary and it does **not** split on commas: repeat the flag to widen the filter
(`--category M5 --category M17`). Translate what the user asks for into those words — "fix my schema"
is `--category M5`, "just the safe ones" is `--category auto` — and say which filter you applied.
`--target` accepts the adapter ids plus the aliases `local`, `shopify`, `wordpress`, `manual`, and theView the complete source on GitHubgeo
When your agent uses it
Analyze and score only a page's AI-search visibility (GEO/AEO) — answer extractability, fact density, AI-crawler access and Google AI-feature snippet eligibility, entity linking, AI discovery files, and agent-readiness — and report an AI Visibility score with a citability breakdown, plus optional unscored probes (web-search presence, Search Console generative-AI impressions). Read-only. Use for "will AI engines cite this?", GEO/AEO, or ChatGPT/Perplexity/Google AI Overviews/Gemini optimization.
What it loads into context
Frontmatter
183 tokensAlways available to the agent
SKILL.md
1,733 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- What to do
- `--probe "<question>"` — web-search presence (repeatable)
- `--gsc-ai-export <csv>` — Search Console generative-AI import
Scripts executed separately
../../scripts/gsc-ai-import.mjs../../scripts/probe-report.mjs
Files
../../scripts/gsc-ai-import.mjsruns10,020 bytes../../scripts/probe-report.mjsruns8,242 bytesSKILL.mdcore6,846 bytesSKILL.mdcore6,846 bytesSKILL.mdcore6,846 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# /claude-seo-ai:geo
The AI-search (GEO/AEO) subset of the audit. **Read-only.**
`$ARGUMENTS` = `<url|path> [flags]`. `--pages N`, `--render static|auto|js` (default `static`) and `--feed <path>` (the Agentic Commerce Protocol product feed; it only does anything when the e-commerce vertical is active, and without it `M18.agentic.acp_feed_errors` stays `needs_api` — an unread feed is never a clean feed) are forwarded to the orchestrator and reach `audit.mjs` unchanged. `--probe` and `--gsc-ai-export` are handled here, not by `audit.mjs`: they are **probes — reported, never scored** (severity 0, excluded from the AI Visibility score and from every cap).
## What to do
1. Invoke **seo-orchestrator** with the target and flags and the instruction "AI path only": it acquires the snapshot with the scripts (never WebFetch), runs the deterministic checks, and dispatches **ai-search-geo-specialist** with **M6** (seo-entity-linking), **M11** (seo-geo-answerblocks), **M12** (seo-geo-factdensity), **M14** (seo-ai-crawlers — access + Google AI-feature eligibility), **M21** (seo-ai-discovery — llms.txt, agents.md, UCP, agentic sitemap), **M22** (seo-agent-readiness); plus **M5** (seo-schema-jsonld, via schema-generator) and **M4** (seo-rendering, via technical-auditor) because they feed AI visibility.
2. Run the probe flows below **only** when their flag is present.
3. Present from `report.json`: the **AI Visibility** score (band + interpretation), the citability breakdown by category, `needs_api` / `manual_review` counts, the prioritized GEO/AEO actions, any `probes` section (clearly separated from the score), and the `report.md` path.
4. Lead with the one thing Google documents: a page reaches AI Overviews / AI Mode by being **indexed and snippet-eligible** in Search (M14 `ai_eligibility`). Remind the user that **M21 — llms.txt and the other discovery files — is reported at weight 0** (Google Search ignores llms.txt; vendor support is partial). Never write files; offer `/claude-seo-ai:fix` for the safe AUTO fixes (e.g. the AI-crawler robots.txt preset).
## `--probe "<question>"` — web-search presence (repeatable)View the complete source on GitHubscore
When your agent uses it
Recompute and display the two scores (Search SEO + AI Visibility) from a persisted audit run, without re-crawling. Use to re-show or refresh the scores after an audit, to score a specific run directory or host, or to score a saved findings JSON file.
What it loads into context
Frontmatter
85 tokensAlways available to the agent
SKILL.md
818 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
Scripts executed separately
../../scripts/checks/_shared.mjs../../scripts/score.mjs../../scripts/score.mjs
Files
../../scripts/checks/_shared.mjsruns17,733 bytes../../scripts/score.mjsruns43,922 bytes../../scripts/score.mjsruns43,922 bytesSKILL.mdcore3,086 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# /claude-seo-ai:score
Recompute and show the two 0–100 scores by running the **seo-score** skill, which uses `scripts/score.mjs` for a reproducible number.
Runs live under `<root>/<host>/<run-id>/`, where `<root>` is `--out` › `$CLAUDE_SEO_AI_HOME` › `${CLAUDE_PLUGIN_DATA}/runs` › `~/.claude-seo-ai/runs`, and `<host>` is the lower-cased host with `:` → `_` (local targets become `local/<basename>-<hash>`). **`score.mjs --run` takes a directory or a findings file — it does not understand the word `latest`**, so resolve the pointer yourself with `Read` before you call it: `<root>/<host>/latest.json` is `{ run, path, updated_at }` and `path` is the absolute run directory; `<root>/index.json` lists every host with its `latest` run id.
- No argument (default): read `<root>/index.json`, take the host whose `latest` run id sorts highest (run ids are UTC `YYYY-MM-DDTHH-mm-ssZ`, so string order is chronological), read that host's `latest.json`, then `node "${CLAUDE_PLUGIN_ROOT}/scripts/score.mjs" --run <path from latest.json>`.
- `latest:<host>` → read `<root>/<host>/latest.json` and pass its `path` to `--run`.
- A run directory → `node "${CLAUDE_PLUGIN_ROOT}/scripts/score.mjs" --run <run-dir>` (it reads `<run-dir>/findings.json`).
- A findings JSON path → `node "${CLAUDE_PLUGIN_ROOT}/scripts/score.mjs" --findings <path>` (add `--vertical a,b`, `--multilingual` and `--environment production|preview|staging|local` when the file carries no run context; `--manifest <crawl.json>` adds the site rollup, `--strict` exits 2 if any finding fails schema validation, `--validate-only` reports the per-finding schema verdict without scoring).
- If no `index.json`/`latest.json` exists, or the run has no `findings.json`, tell the user to run `/claude-seo-ai:audit <url>` first — never score from memory.
Show both scores with bands, the per-category breakdown, any severity-gating cap (`cap_reasons`), the `unscored` state when no category is active, and the `needs_api` / `manual_review` / `dropped` counts. Two scores, never blended. When an axis comes back `provisional: true` (`state: "partial"`), say the band and its `coverage` % in the same sentence — a band built on a third of the model is not the same claim as a measured one, and the per-axis `warnings[]` name what was not measured. With a rollup, read `pages_scored` against `pages_count`: a page listed in `unscored_pages[]` did not answer 2xx, so it was never scored — name those pages and their status instead of letting the site score stand for a sample that was not measured.View the complete source on GitHubseo-agent-readiness
When your agent uses it
Audit how well a page can be operated by AI agents and agentic browsers — semantic interactive controls (<button>/<a href> instead of div/span click handlers), named buttons and links, labeled form controls, no javascript: hrefs, primary content outside iframes, and WebMCP detection — following Google's generative-AI guidance and the web.dev agent-friendly websites guide. Module M22 "Agent-readiness". Feeds the AI Visibility score (weight 4, all directional).
What it loads into context
Frontmatter
121 tokensAlways available to the agent
SKILL.md
1,701 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/agent-readiness.mjs../../scripts/agent-readiness.mjs../../scripts/agent-readiness.mjs../../scripts/agent-readiness.mjs../../scripts/agent-readiness.mjs../../scripts/agent-readiness.mjs../../scripts/agent-readiness.mjs../../scripts/agent-readiness.mjs../../scripts/agent-readiness.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/agent-readiness.mjsruns18,185 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore6,815 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-agent-readiness (M22 — Agent-readiness)
Google's *Optimizing your website for generative AI features* guide points to "agent-friendly website best practices" (web.dev *Build agent-friendly websites*): agents drive a page through its accessibility tree, so unnamed, non-semantic, or script-only controls are invisible to them. This module checks the statically checkable subset and names the rest. Alt text is M9, anchor-text quality is M10, rendering is M4 — reference them, never re-emit them.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` (`anchors[]`, `forms`, `iframes`, `scripts[]`, `landmarks`) from `<run_dir>/pages/<slug>.json`, preferring `parsed_rendered` when `render.used` is not `none`; Grep `pages/<slug>.html` (or `.rendered.html`) for the offending elements as verbatim evidence. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
1. **Semantic controls** — interactive behavior on `div`/`span` (`onclick`, `role="button"`, `tabindex` + handler) instead of `<button>` / `<a href>`; count against total controls.
2. **Named buttons** — `<button>`, `role="button"`, `input[type=button|submit]` with no text content, `aria-label`, `aria-labelledby`, `title`, or `value`.
3. **Named links** — `<a href>` whose accessible name is empty (icon-only, image without alt inside). Generic-vs-descriptive anchor wording stays in M10.
4. **Labeled form controls** — `input`/`select`/`textarea` without `<label for>`, a wrapping `<label>`, `aria-label`, or `aria-labelledby` (a placeholder alone does not count).
5. **`href="javascript:"`** (and `href="#"` carrying handlers) — navigation that only works through script.View the complete source on GitHubseo-ai-crawlers
When your agent uses it
Audit AI crawler access and Google AI-feature eligibility for a page — check the snippet controls that actually gate AI Overviews and AI Mode (noindex, nosnippet, max-snippet, data-nosnippet), resolve per-bot effective access from robots.txt for the audited path (training vs retrieval vs user-fetch tokens), read Content-Signal posture, diff what different user-agents are served, confirm non-JS crawlers can read the page, and generate a choice-gated robots.txt preset. Module M14. Feeds the AI Visibility score.
What it loads into context
Frontmatter
136 tokensAlways available to the agent
SKILL.md
3,875 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/ai-eligibility.mjs../../scripts/ai-eligibility.mjs../../scripts/ai-eligibility.mjs../../scripts/ai-eligibility.mjs../../scripts/ai-eligibility.mjs../../scripts/ai-eligibility.mjs../../scripts/ai-eligibility.mjs../../scripts/ai-eligibility.mjs../../scripts/parse-robots-sitemap.mjs../../scripts/parse-robots-sitemap.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/ua-diff.mjs../../scripts/ua-diff.mjs
Files
../../scripts/ai-eligibility.mjsruns13,826 bytes../../scripts/ai-eligibility.mjsruns13,826 bytes../../scripts/ai-eligibility.mjsruns13,826 bytes../../scripts/ai-eligibility.mjsruns13,826 bytes../../scripts/ai-eligibility.mjsruns13,826 bytes../../scripts/ai-eligibility.mjsruns13,826 bytes../../scripts/ai-eligibility.mjsruns13,826 bytes../../scripts/ai-eligibility.mjsruns13,826 bytes../../scripts/parse-robots-sitemap.mjsruns11,144 bytes../../scripts/parse-robots-sitemap.mjsruns11,144 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/ua-diff.mjsruns8,595 bytes../../scripts/ua-diff.mjsruns8,595 bytesSKILL.mdcore14,974 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-ai-crawlers (M14)
Two separate questions live in this module, and only one of them has vendor documentation behind it:
1. **Google's AI features.** A page can appear in AI Overviews / AI Mode only if it is indexed and eligible to be shown in Google Search **with a snippet**. Those gates are documented, so `M14.ai_eligibility.*` carries the only `established` findings here.
2. **Third-party AI crawlers.** Who may fetch and cite the page — OpenAI, Anthropic, Perplexity, Apple, Amazon, Meta, Mistral, DuckDuckGo, Cohere. Access is documented per user-agent; the citation *effect* is not, so those findings stay `directional`.
The training-vs-retrieval-vs-user-fetch distinction decides every robots recommendation. Reference: `references/ai-crawlers.md` (UA table with sources, Content-Signal, eligibility gates, presets). AI discovery files (llms.txt, agents.md, `/.well-known/ucp`, agentic sitemap) are module M21 — see `seo-ai-discovery`; agentic-commerce readiness is M18 (`seo-ecommerce`).
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` plus `robots_directives`, `headers` (`x-robots-tag`), and `render{needed,used,delta}`; Grep `pages/<slug>.html` for verbatim evidence. `<run_dir>/site/robots.json` carries, for the **audited path**, `verdicts`, `effective_access{[bot]:{class, fetch, url_allowed, via, rule, signals, doc_url, robots_reliability}}`, `content_signals{present, placement, global, by_group[], malformed[], cloudflare_managed_hint}`, `groups`, and `ua_table_version`. When the run included a user-agent diff, `<run_dir>/ua-diff.json` holds per-UA status/title/h1/word_count/canonical/robots/jsonld. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## AuditsView the complete source on GitHubseo-ai-discovery
When your agent uses it
Audit AI discovery files and agent endpoints at the site root — /llms.txt and /llms-full.txt structure and link health, /agents.md, the UCP merchant profile at /.well-known/ucp (only when no ecommerce module owns it), the ARD /.well-known/ai-catalog.json, and /sitemap_agentic_discovery.xml — plus a robots cross-check that these files are not blocked for the bots meant to read them. Module M21 "AI discovery & agent endpoints" (weight 0 — reported, never scored). Appears in the AI Visibility report only.
What it loads into context
Frontmatter
142 tokensAlways available to the agent
SKILL.md
1,792 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore6,743 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-ai-discovery (M21 — AI discovery & agent endpoints)
Discovery files tell agents and coding assistants where to look; none of them is a documented answer-engine ranking or citation signal, so every M21 finding is `speculative` or `directional` and the module's weight is **0**. Robots/UA posture lives in `seo-ai-crawlers` (M14); agentic-commerce readiness (UCP + catalog feeds on stores) lives in `seo-ecommerce` (M18).
## Inputs
Work from the PageSnapshot named in your dispatch envelope: `<run_dir>/site/discovery.json` holds the probe results (status, content-type, bytes, raw bodies ≤256 KB) for `/llms.txt`, `/llms-full.txt`, `/agents.md`, `/.well-known/ucp`, `/.well-known/ai-catalog.json`, `/sitemap_agentic_discovery.xml`, `/api/ucp/mcp`; `<run_dir>/site/robots.json` holds the per-bot verdicts for the robots cross-check; `vertical` in the envelope decides UCP ownership. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
1. **llms.txt** — present (200, text); llmstxt.org structure: one H1 title, optional summary blockquote, H2 sections of `- [title](url): note` links; linked URLs resolve (`--deep`, ≤25 checked); the file is not disallowed in robots to the AI bots that would read it. `/llms-full.txt` is reported present/absent only.
2. **agents.md** — present/absent; note when it is a platform default (Shopify ships one) — informational.
3. **UCP profile** (`/.well-known/ucp`) — **only when `ecommerce` is not active** (M18 owns it otherwise): public (200, no auth), JSON, `ucp.version` as `YYYY-MM-DD`, `services`/`capabilities` objects keyed by reverse-DNS names → arrays of `{version, spec, transport?, endpoint?, schema}`. Note a declared `dev.shopify.catalog` entry — it says the platform exposes a catalog service, which is a platform default, not a merchant decision.
4. **ARD catalog** (`/.well-known/ai-catalog.json`) — present and parseable JSON. Report-only: v0.9 draft (2026-05-28), near-zero adoption.
5. **Agentic sitemap** (`/sitemap_agentic_discovery.xml`) — present, well-formed XML, sampled `<loc>` entries resolve.View the complete source on GitHubseo-core-web-vitals
When your agent uses it
Audit Core Web Vitals & page performance — measure LCP, INP, and CLS against p75 field thresholds, diagnose render-blocking resources, unoptimized images, and layout-shift sources, and produce prioritized, advisory-only remediation guidance. Module M15. Feeds the Search SEO score (heavily) and the AI Visibility score (minimally).
What it loads into context
Frontmatter
98 tokensAlways available to the agent
SKILL.md
1,635 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes (fixable: advisory)
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/psi-client.mjs../../scripts/psi-client.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/psi-client.mjsruns6,355 bytes../../scripts/psi-client.mjsruns6,355 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore6,408 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-core-web-vitals (M15)
Core Web Vitals are a confirmed Google ranking signal and a proxy for page quality. Thresholds, field-vs-lab honesty rules, and LCP decomposition: `references/cwv-thresholds.md` (follow it exactly).
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`scripts[]`, `stylesheets[]`, `images[]`) plus `timing.ttfb_ms`, `body.bytes`, and `render` — field data still needs the PSI tier; Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Field CWV at p75** — LCP ≤ 2.5 s, INP ≤ 200 ms (INP replaced FID), CLS ≤ 0.1, all at the 75th percentile of real-user data. All three must pass for a "good" rating. Use the proactive-warning thresholds (LCP > 2.0 s, INP > 160 ms, CLS > 0.08) to `warn` before failing.
2. **Render-blocking resources** — synchronous `<script>` in `<head>` (no `defer`/`async`/`type=module`), blocking `<link rel="stylesheet">`, and `@import` chains that delay first render (LCP/INP risk).
3. **Unoptimized images** — missing `width`/`height` (or `aspect-ratio`), no `loading="lazy"` below the fold, no responsive `srcset`/`sizes`, legacy formats where AVIF/WebP would serve, and a non-preloaded LCP image.
4. **Layout-shift sources** — images/iframes/ads/embeds without reserved dimensions, web-font swap without `font-display`/size-adjust, and content injected above existing content (CLS risk).View the complete source on GitHubseo-crawl-render
When your agent uses it
Acquire a page or site into the shared PageSnapshot every audit module reads — real HTTP status chain, headers, robots directives, raw HTML on disk, parsed document, optional headless-Chrome render with a raw-vs-rendered delta, and once-per-run site artifacts (robots, sitemaps, AI discovery files). Acquisition is done by the bundled scripts (snapshot.mjs / crawl.mjs), never by WebFetch. Records the data tier reached.
What it loads into context
Frontmatter
112 tokensAlways available to the agent
SKILL.md
1,502 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Acquisition
- Render decision (lives in the script)
- PageSnapshot v2 — `<run_dir>/pages/<slug>.json`
- Tiers
Scripts executed separately
../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/crawl.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/crawl.mjsruns48,090 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore5,624 bytesSKILL.mdcore5,624 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-crawl-render
Produces the `PageSnapshot` (v2) consumed by every module. The **scripts** fetch, parse, render, and persist; this skill decides which command to run and reads only its compact stdout summary. Claude Code's `WebFetch` returns a lossy model summary with no headers, status codes, or raw HTML and does not follow cross-host redirects — it is never the acquisition path.
## Acquisition
- Single page or local path: `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <url|path> --out "${CLAUDE_PLUGIN_DATA}/runs" [--render auto|static|js] [--ua default|googlebot|bingbot|gptbot|oai-searchbot|claude-searchbot] [--renderer auto|chrome|playwright|none] [--rendered-file <dom.html>] [--max-bytes 5000000] [--json]` — a non-default `--ua` writes `pages/<slug>.ua-<preset>.json`.
- Multi-page: `node "${CLAUDE_PLUGIN_ROOT}/scripts/crawl.mjs" <url> [--pages 12] [--max 40] [--per-template 2] [--depth 3] --out "${CLAUDE_PLUGIN_DATA}/runs"` — homepage + target always, then at most `--per-template` pages per detected template, robots respected; every skip is logged in `crawl.json.sampling`. Also takes `--concurrency`, `--delay`, `--include`/`--exclude`, `--seeds`, `--sitemap-sample`, `--link-status` and `--no-robots`.
- Read the stdout summary only (run dir, slugs, counts, render verdict, warnings). **Never paste HTML into the conversation** — modules `Read` `pages/<slug>.json` and `Grep` `pages/<slug>.html`.
- Local directories are read as built HTML (`status: null`, empty headers). Framework source with no built output exits 2: build first or give the deployed URL.
## Render decision (lives in the script)
`--render auto` marks a render as needed on word count <150, hydration markers (`__NEXT_DATA__`, `__NUXT_DATA__`, `self.__next_f`, empty `#app`/`#__next` root) on a still-thin page, no h1 with <3 anchors, or a `<noscript>` JS notice. `--render static` never renders; `--render js` always tries. **The default differs by entry point**: `snapshot.mjs` defaults to `auto`, while `crawl.mjs` and `audit.mjs` default to `static` — a headless or multi-page run never launches a browser unless it is asked to.
- `--renderer auto` (the default) auto-detects headless Chrome/Chromium/Edge/Brave (`$CLAUDE_SEO_AI_CHROME` → `$CHROME_PATH` → `$PUPPETEER_EXECUTABLE_PATH` → PATH → default app paths → Playwright caches); `--renderer playwright` is opt-in and used only when already installed — **never auto-installed**; `--renderer none` disables rendering entirely; `--rendered-file <html>` accepts a DOM captured by a Playwright/Firecrawl MCP or by the user.View the complete source on GitHubseo-crawlability
When your agent uses it
Audit and generate robots.txt and general crawl access for a page — verify robots.txt reachability and syntax, detect Disallow rules that block CSS/JS or important content, sanity-check crawl-delay, confirm a Sitemap directive, and assert overall crawl access for Googlebot/Bingbot. Module M1. Feeds the Search SEO score.
What it loads into context
Frontmatter
90 tokensAlways available to the agent
SKILL.md
1,857 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/parse-robots-sitemap.mjs../../scripts/parse-robots-sitemap.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/parse-robots-sitemap.mjsruns11,144 bytes../../scripts/parse-robots-sitemap.mjsruns11,144 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore7,288 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-crawlability (M1)
Crawl access is the precondition for every other search signal: if Googlebot/Bingbot can't fetch the page and its assets, nothing else ranks. This module covers general-purpose crawl access only. AI-specific bot directives (GPTBot, Claude-SearchBot, etc.) live in `seo-ai-crawlers` (M14) and AI discovery files (`llms.txt`, `agents.md`, UCP) in `seo-ai-discovery` (M21); see `references/ai-crawlers.md` for that boundary.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` plus `status_chain` and `headers`, and the parsed robots file in `<run_dir>/site/robots.json` (syntax, groups, Googlebot/Bingbot verdicts for the audited URL, `Sitemap:` lines); Grep `pages/<slug>.html` for verbatim evidence. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`) plus `site/robots.json`:
1. **Reachability**: `/robots.txt` returns 200 (a 404 means "allow all" but is worth flagging; a 5xx can suspend crawling).
2. **Syntax**: each line is a valid directive (`User-agent`, `Disallow`, `Allow`, `Sitemap`, `Crawl-delay`); flag unknown tokens, missing `User-agent` group headers, and BOM/encoding issues.
3. **Asset blocking**: any `Disallow` that blocks CSS/JS, fonts, or `/wp-includes/`-style paths — this breaks rendering and is a leading cause of "page looks broken to Google" (cross-check with M-render).
4. **Content blocking**: `Disallow` rules that hide important indexable paths from `Googlebot`/`Bingbot`.View the complete source on GitHubseo-ecommerce
When your agent uses it
Conditionally audit and generate e-commerce structured data and agentic-commerce readiness for product pages — validate Product/Offer (price, priceCurrency, availability), AggregateRating/Review, faceted-navigation canonicalization and variant/ProductGroup handling, cross-check page-to-schema-to-feed consistency, and assess whether shopping agents can read the store (UCP merchant profile, catalog eligibility, ACP feed hygiene). Module M18. Feeds both the Search SEO and AI Visibility scores.
What it loads into context
Frontmatter
114 tokensAlways available to the agent
SKILL.md
2,883 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/acp-feed-lint.mjs../../scripts/acp-feed-lint.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/ai-discovery.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/validate-jsonld.mjs../../scripts/validate-jsonld.mjs../../scripts/validate-jsonld.mjs
Files
../../scripts/acp-feed-lint.mjsruns12,560 bytes../../scripts/acp-feed-lint.mjsruns12,560 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/ai-discovery.mjsruns29,279 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/validate-jsonld.mjsruns6,725 bytes../../scripts/validate-jsonld.mjsruns6,725 bytes../../scripts/validate-jsonld.mjsruns6,725 bytesSKILL.mdcore11,985 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-ecommerce (M18)
Conditional commerce module: it runs only when commerce signals are detected, then audits (a) product structured data for Search and (b) whether an AI shopping agent can actually read and act on the store. Reference: `references/schema-tier1.md` (Product + Offer, Review/AggregateRating rows). Platform-specific ids — Shopify's Catalog and UCP defaults among them — live in `references/platforms/shopify.md` and are emitted by the platform-conditional checks, not from here.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`jsonld[]`, `metas[]`, `text_sample`) and `vertical` from the envelope — the commerce gate is decided there; `<run_dir>/site/discovery.json` carries the probes for `/.well-known/ucp` and `/api/ucp/mcp` (status, content-type, body); `<run_dir>/profile.json` names the platform. Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Commerce gate**: detect commerce signals (price/currency text, add-to-cart/buy controls, `Product`/`Offer` schema, `og:type=product`). If none, every M18 finding is `not_applicable` (severity 0) — do not force product schema onto non-commerce pages.
2. **Product + Offer**: `Product` has `name`, `image`; `Offer` has `price`, `priceCurrency`, `availability` (`schema.org/InStock` etc.). Per `references/schema-tier1.md`.
3. **AggregateRating / Review**: if ratings are visibly shown, expect `AggregateRating` (`ratingValue`, `reviewCount`/`bestRating`) and/or `Review` (`author`). Never mark up ratings that are not visible on the page.
4. **Page → schema → feed consistency**: schema `price`/`priceCurrency`/`availability` must match the visible on-page values and the merchant feed. Mismatches risk disapproval and lost rich results.View the complete source on GitHubseo-eeat
When your agent uses it
Audit and strengthen E-E-A-T and trust signals on a page — verify author identity/credentials (Person schema, byline, author page, sameAs), Organization about/contact/policies, visible experience/expertise markers, and transparency (sourcing, disclosures), and generate Person/Organization trust JSON-LD. Module M16. Feeds both the Search SEO and AI Visibility scores.
What it loads into context
Frontmatter
99 tokensAlways available to the agent
SKILL.md
1,375 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/validate-jsonld.mjs../../scripts/validate-jsonld.mjs../../scripts/validate-jsonld.mjs
Files
../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/validate-jsonld.mjsruns6,725 bytes../../scripts/validate-jsonld.mjsruns6,725 bytes../../scripts/validate-jsonld.mjsruns6,725 bytesSKILL.mdcore5,670 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-eeat (M16)
Experience, Expertise, Authoritativeness, and Trust are how both Google's quality systems and AI answer engines decide whether to rely on a page. Trust is foundational and now applies beyond YMYL. Schema details: `references/schema-tier1.md`.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`jsonld[]`, `anchors[]`, `text_sample`); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Author identity & credentials**: is there a visible byline? A `Person` schema with `name`, `jobTitle`, `knowsAbout`, and `sameAs[]` (LinkedIn/Wikidata)? Does the byline link to an author/bio page? Are credentials/experience stated, not just a name?
2. **Organization trust**: discoverable About and Contact pages; an `Organization` block with `name`, `url`, `logo`, `contactPoint`, `sameAs[]`; visible editorial/privacy/returns policies appropriate to the vertical.
3. **Experience & expertise markers**: first-hand signals (original photos, "we tested", dates, methodology) and topical depth — not just generic prose.
4. **Transparency**: sourcing/citations for claims, author disclosures (affiliate, sponsored, AI-assisted), last-reviewed dates. Defer the `sameAs` identity-graph detail to M6/seo-entity-linking.View the complete source on GitHubseo-entity-linking
When your agent uses it
Audit and generate entity & knowledge-graph linkage for a page — verify sameAs identity links (Wikidata/Wikipedia/LinkedIn/Crunchbase/official profiles) on Organization/Person, check NAP/entity consistency for local, confirm a defined primary entity, and measure entity density, then propose ready-to-inject sameAs/about/mentions references. Module M6. Feeds the AI Visibility score.
What it loads into context
Frontmatter
106 tokensAlways available to the agent
SKILL.md
1,358 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore5,425 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-entity-linking (M6)
Entity linkage tells search engines and AI systems *which real-world thing* a page is about and ties it to the Knowledge Graph. Reference: `references/schema-tier1.md` (the `sameAs`/`@id`/Person+Organization rules). This module owns the `sameAs` detail M5 defers here.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`jsonld[]`, `anchors[]`); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **sameAs identity links**: for `Organization`/`Person` entities, check for `sameAs[]` resolving to canonical identities — Wikidata QID, Wikipedia, LinkedIn, Crunchbase, official social/company profiles. Wikidata is the strongest Knowledge Graph target.
2. **Primary entity defined**: confirm exactly one clear primary entity per page (via `@id` + `mainEntity`/`mainEntityOfPage`), not an ambiguous or missing subject.
3. **Entity consistency (local)**: for `LocalBusiness`, verify NAP (name, address, telephone) in schema matches visible on-page NAP and is internally consistent across blocks.
4. **Entity density**: count distinct linked/marked entities (`@id`d nodes, `sameAs` targets, `about`/`mentions`) — flag thin pages with no resolvable entity references.View the complete source on GitHubseo-fix-apply
When your agent uses it
Writer protocol preloaded into the seo-fixer-writer agent — how confirmed SEO/AI-search fixes are applied (git pre-flight, backup, Edit/Write for local diffs, ticketed adapter CLIs for remote targets, re-verify, publish only on a second ticket, rollback) and the findings-array output contract. Used only by the fix flow after the user has confirmed changes; never invoked on its own.
What it loads into context
Frontmatter
111 tokensAlways available to the agent
SKILL.md
1,882 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Protocol (per confirmed change, in order)
- Output contract
- Hard rules
Files
SKILL.mdcore7,229 bytesSKILL.mdcore7,229 bytesSKILL.mdcore7,229 bytesSKILL.mdcore7,229 bytesSKILL.mdcore7,229 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-fix-apply (writer protocol)
This file is the authoritative protocol; `agents/seo-fixer-writer.md` carries the in-context
summary — keep the two in sync. Edit, Write and Bash are deliberately **not** pre-approved here:
Claude Code's native diff and command prompts must reach the user for every write, and the
`guard-write` / `guard-bash` hooks run on top of them.
You apply what was already confirmed. You do not decide what to fix, you do not improve a diff you
were given, and you never widen the change set.
## Inputs
The `fix` command dispatches you with a JSON envelope:
View the complete source on GitHubseo-freshness
When your agent uses it
Audit and repair freshness & temporal signals on a page — reconcile visible publish/update dates with schema datePublished/dateModified, flag staleness against topic volatility, and inject honest dateModified. Module M13. Feeds both the Search SEO and AI Visibility scores.
What it loads into context
Frontmatter
75 tokensAlways available to the agent
SKILL.md
1,385 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/check-freshness.mjs../../scripts/check-freshness.mjs../../scripts/check-freshness.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/check-freshness.mjsruns10,452 bytes../../scripts/check-freshness.mjsruns10,452 bytes../../scripts/check-freshness.mjsruns10,452 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore5,704 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-freshness (M13)
Freshness is a recency signal both classic ranking systems (Query Deserves Freshness) and AI answer engines weigh — Perplexity in particular favours recently-updated sources when citing. Date fields tie directly to Article schema (cross-check M5); see `references/schema-tier1.md` for the date rules.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`jsonld[]`, `metas[]` for `article:published_time`/`modified_time`) plus `headers.last-modified`; Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Visible dates**: detect on-page "Published" / "Updated on" / "Last reviewed" patterns and their values (ISO or human-readable).
2. **Schema dates**: parse `datePublished`/`dateModified` from JSON-LD `Article`/`BlogPosting`/`NewsArticle`.
3. **Agreement**: visible date and schema date must match; flag mismatches and schema dates with no visible counterpart (AI engines distrust hidden-only dates).
4. **Staleness**: estimate content age (most recent reliable date) vs topic volatility — fast-moving topics (prices, tooling, "best X 2026", regulations) decay faster than evergreen reference content. Report stale, not just old.View the complete source on GitHubseo-geo-answerblocks
When your agent uses it
Audit and generate answer-extractable content — verify each question heading is followed by a ~40-60 word direct answer, passages are self-contained (~134-167 words, no unresolved anaphora), and lists/tables/definitions/TL;DR blocks exist; draft answer-block and summary rewrites for the user to accept. Module M11. Feeds the AI Visibility score.
What it loads into context
Frontmatter
102 tokensAlways available to the agent
SKILL.md
1,693 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/check-answerblocks.mjs../../scripts/check-answerblocks.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/check-answerblocks.mjsruns5,789 bytes../../scripts/check-answerblocks.mjsruns5,789 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore7,184 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-geo-answerblocks (M11)
AI engines cite passages they can lift whole, without surrounding context — so passage structure is the highest-leverage on-page AI signal. This module checks extractability; for who is allowed to fetch and cite the page, see `references/ai-crawlers.md`.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`headings[]`, `text_sample`, `word_count`) — passages come from the HTML file, not from a summary; Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Heading → direct answer**: for each H2/H3 that reads as a question (or implies one), is the first paragraph a direct, ~40-60 word answer — not a windup ("In this section we'll explore…")?
2. **Self-contained passages**: are top-level passages ~134-167 words and answerable on their own? Flag unresolved anaphora ("as mentioned above", "this", "the latter") that needs prior context to parse.
3. **Structured forms**: presence of lists, comparison tables, and explicit term **definitions** where the content warrants them — these are disproportionately quoted.
4. **Question-shaped headings**: do headings phrase real user questions (how/what/why/when), matching likely prompts?View the complete source on GitHubseo-geo-factdensity
When your agent uses it
Audit fact density and sourcing on a page — measure statistic/number density per passage, detect proprietary/original data, count outbound citations to authoritative sources, and flag claims made without a supporting stat or source. Module M12. Feeds the AI Visibility score. Advisory-only; never fabricates statistics or sources.
What it loads into context
Frontmatter
87 tokensAlways available to the agent
SKILL.md
1,674 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes (fixable: advisory)
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/factdensity.mjs../../scripts/factdensity.mjs../../scripts/factdensity.mjs../../scripts/factdensity.mjs../../scripts/factdensity.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/factdensity.mjsruns5,227 bytes../../scripts/factdensity.mjsruns5,227 bytes../../scripts/factdensity.mjsruns5,227 bytes../../scripts/factdensity.mjsruns5,227 bytes../../scripts/factdensity.mjsruns5,227 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore7,190 bytesSKILL.mdcore7,190 bytesSKILL.mdcore7,190 bytesSKILL.mdcore7,190 bytesSKILL.mdcore7,190 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-geo-factdensity (M12)
Generative engines preferentially cite passages that are concrete, quantified, and attributable. This module measures how "citable" the page's prose is — facts, numbers, original data, and authoritative outbound links — not vocabulary. AI retrieval/citation context: `references/ai-crawlers.md`.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`anchors[]` for outbound links, `text_sample`) and the HTML file for passage-level counts; Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Statistic/number density** per passage: tokenize the main content into passages (paragraph / `<li>` / heading-bounded block) and count numeric tokens — figures, percentages, dates, quantities, ranges. Flag long passages of pure assertion with zero numeric support.
2. **Proprietary/original data**: detect first-party-data signals — patterns like "our study", "our survey", "our data", "we analyzed", "we surveyed", "in our test", "internal data" — and note whether such claims are backed by a method/sample, a table, or a chart.
3. **Outbound citations**: count outbound links from the main content to authoritative sources (standards bodies, primary research, official docs, `.gov`/`.edu`, named publications); distinguish them from internal/nav/affiliate links.
4. **Claim-without-source flags**: detect strong factual or comparative claims ("the most", "fastest", "studies show", superlatives, hard numbers) that carry no inline citation or data reference, and mark each as a candidate for sourcing.View the complete source on GitHubseo-headings-structure
When your agent uses it
Audit and propose fixes for a page's heading hierarchy and semantic outline — verify exactly one H1, logical H2-H6 nesting with no skipped levels, descriptive heading text, and HTML5 landmarks (article/section/nav/main/footer) — so the document outline is extractable. Module M7c. Feeds the Search SEO score (clean headings also support AI answer-block extraction, which is scored separately under M11).
What it loads into context
Frontmatter
109 tokensAlways available to the agent
SKILL.md
1,394 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/parse-html.mjs../../scripts/parse-html.mjs../../scripts/parse-html.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/parse-html.mjsruns7,444 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore5,689 bytesSKILL.mdcore5,689 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-headings-structure (M7c)
A clean, single-rooted heading tree and proper semantic landmarks make a page's outline machine-readable — the same structure that AI engines use to extract answer blocks and that search engines use to understand content scope. Schema/entity context: `references/schema-tier1.md`.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`headings[]` with `region`, `landmarks`); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Single H1**: exactly one `<h1>` per page. Zero H1s, or multiple H1s, are both findings.
2. **No skipped levels**: nesting descends one level at a time (an `<h2>` may be followed by `<h3>`, not directly by `<h4>`). Build the outline and flag any jump.
3. **Descriptive text**: each heading names its section in human terms — flag empty headings, headings used purely for visual styling, and generic placeholders ("Section 1", "Welcome").
4. **Semantic landmarks**: detect `<main>`, `<article>`, `<section>`, `<nav>`, `<header>`, `<footer>`; flag when headings live inside non-semantic `<div>` soup or when more than one `<main>` exists.View the complete source on GitHubseo-images-media
When your agent uses it
Audit and fix image & media accessibility for a page — detect missing/empty/duplicated/keyword-stuffed alt text, check alt quality and length, missing width/height (CLS), legacy formats, and absent VideoObject schema; generate contextual alt and dimensions. Module M9. Feeds both the Search SEO and AI Visibility scores.
What it loads into context
Frontmatter
90 tokensAlways available to the agent
SKILL.md
1,320 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/parse-html.mjs../../scripts/parse-html.mjs../../scripts/parse-html.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/parse-html.mjsruns7,444 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore5,308 bytesSKILL.mdcore5,308 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-images-media (M9)
Accessible, well-described media is read by both ranking systems and AI extractors — alt text is the primary semantic handle for an image. Reference: `references/schema-tier1.md` (VideoObject row).
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`images[]` with `alt_present`/`sized`/`lazy`, `iframes`, `jsonld[]`); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Alt presence**: every content `<img>` has an `alt` attribute. Distinguish decorative images (`alt=""` + `role="presentation"` is correct, not a finding) from missing `alt` entirely (fail).
2. **Alt quality**: not empty on a content image; not the filename (`IMG_2031.jpg`); not duplicated verbatim across distinct images; not keyword-stuffed (comma-separated keyword lists are an anti-pattern). Alt should be descriptive and contextual to surrounding content. Target length ~80-140 chars — long enough to be specific, short enough that screen readers/AI don't truncate.
3. **Dimensions**: `width` and `height` set on `<img>` (or aspect-ratio reserved in CSS) to prevent layout shift (CLS, a Core Web Vitals input — defer the CWV measurement itself to the perf module).
4. **Format**: flag legacy `jpg`/`png` where a modern format (WebP/AVIF) or a responsive `<picture>`/`srcset` would serve smaller bytes.View the complete source on GitHubseo-indexability
When your agent uses it
Audit a page's indexability and site health — canonical presence/validity (self vs cross-domain vs chain, canonical to redirect/404, and the genuinely lethal canonical+noindex pair where the canonical points elsewhere), robots meta and X-Robots-Tag noindex/nofollow, duplicate clusters, pagination signals, plus redirect chains/loops, 4xx/5xx and soft-404 internal links, mixed content, HTTP-to-HTTPS enforcement, user-agent content divergence (cloaking), orphan pages and click-depth — and generate self-referential canonical / noindex-removal fixes. Module M2 (covers M3 site health). Feeds the Search SEO score.
What it loads into context
Frontmatter
161 tokensAlways available to the agent
SKILL.md
2,542 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/ua-diff.mjs../../scripts/ua-diff.mjs
Files
../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/ua-diff.mjsruns8,595 bytes../../scripts/ua-diff.mjsruns8,595 bytesSKILL.mdcore10,481 bytesSKILL.mdcore10,481 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-indexability (M2)
If a page can't be crawled, indexed, or canonicalized correctly, every other signal is wasted — this is the floor under the Search score. Schema-side context: `references/schema-tier1.md`. AI-bot access and snippet eligibility are M14 (`seo-ai-crawlers`); this module owns the classic-Search side, including the search-axis cloaking check.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`canonicals[]`, `robots_meta[]`) plus `status_chain`, `headers`, `robots_directives.effective`, and the crawl graph in `<run_dir>/crawl.json` for orphans, depth, duplicate clusters, and internal link status; `<run_dir>/profile.json` carries `environment.kind` (`production|preview|staging|local`); when the run included a user-agent diff, `<run_dir>/ua-diff.json` holds the per-UA response fields. Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`) plus `headers`, `status_chain`, and `robots_directives`:
1. **Canonical**: exactly one `<link rel="canonical">`; absolute HTTPS URL; classify self-referential vs cross-domain vs chained (canonical points to a URL that itself canonicalizes elsewhere). Flag a canonical that resolves to a redirect or a 4xx.
2. **Canonical + noindex — read it correctly.** `noindex` together with a **self-referential** canonical is *not* the lethal pair: it is a coherent, usually deliberate exclusion (thank-you pages, filtered views, staging). Report it as `M2.robots.noindex_present` and ask before touching it. The pair is lethal only when the canonical points **elsewhere**: the page tells Google "index that other URL instead" while also saying "don't index me", and the contradiction can propagate the `noindex` to the canonical target — that is `M2.canonical.noindex_conflict`.
3. **Robots directives**: `<meta name="robots">` and the `X-Robots-Tag` header for `noindex`/`nofollow`/`none`; reconcile header vs meta (the most restrictive applies; a header directive reaches non-HTML responses too). Record the **scope** — a directive emitted by a template or an edge rule is `template`/`site`, one written into a single document is `page`.
4. **Duplicate clusters**: near-identical title/H1/body across URLs with no consolidating canonical.View the complete source on GitHubseo-internal-linking
When your agent uses it
Audit and improve a site's internal-linking topology and semantic HTML — map pillar/cluster structure, count and grade contextual in-body links and anchor text, surface orphan pages and nav-heavy templates, and propose specific source→target link insertions. Module M10. Feeds the Search SEO score.
What it loads into context
Frontmatter
83 tokensAlways available to the agent
SKILL.md
1,463 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/link-graph.mjs../../scripts/link-graph.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/link-graph.mjsruns8,319 bytes../../scripts/link-graph.mjsruns8,319 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore5,985 bytesSKILL.mdcore5,985 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-internal-linking (M10)
Internal links distribute crawl priority and topical authority and tell search engines (and AI crawlers) how your pages relate. Reference: `references/schema-tier1.md` for the entity/`@id` linkage this topology should mirror; M6/seo-entity-linking owns external `sameAs`.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`anchors[]` with `region`/`internal`/`anchor`, `landmarks`) and the crawl graph in `<run_dir>/crawl.json` (`graph.edges`, `pages[].inlinks`, `templates[]`); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Pillar/cluster topology**: classify each URL (pillar hub vs. cluster page) and confirm clusters link up to their pillar and the pillar links down to its cluster — gaps break the hub-and-spoke model.
2. **Contextual in-body links**: count links inside `<main>`/`<article>` prose (target ~3-5 descriptive in-content links per page); flag pages with zero in-body internal links.
3. **Anchor-text descriptiveness**: flag generic anchors ("click here", "read more", "learn more", bare URLs) and over-optimized exact-match repetition; anchors should describe the destination.
4. **Orphan pages**: list URLs in the sitemap/crawl with no incoming internal links from other crawled pages.View the complete source on GitHubseo-international
When your agent uses it
Audit and generate hreflang annotations for multilingual sites — check reciprocity, BCP-47 validity, self-reference, x-default, hreflang/canonical conflicts, and <html lang> agreement, and emit reciprocal hreflang link sets. Module M20 (conditional). Feeds the Search SEO score.
What it loads into context
Frontmatter
85 tokensAlways available to the agent
SKILL.md
1,825 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/hreflang-check.mjs../../scripts/hreflang-check.mjs../../scripts/hreflang-check.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/hreflang-check.mjsruns3,269 bytes../../scripts/hreflang-check.mjsruns3,269 bytes../../scripts/hreflang-check.mjsruns3,269 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore7,105 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-international (M20)
hreflang tells search engines which language/region URL to serve. This module is **conditional**: it only runs when seo-vertical-detect flags a multilingual site (multiple `lang`/locale URLs, language switcher, or existing hreflang). On monolingual sites every finding is `not_applicable` at severity 0. Schema-type concerns defer to `references/schema-tier1.md`; this module owns link-level localization only.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`hreflang[]`, `canonicals[]`, `<html lang>` in `parsed`) plus `headers.link` and the sibling pages under `<run_dir>/pages/` for reciprocity; Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`). Read hreflang from `<link rel="alternate" hreflang="...">` in `<head>` (also accept HTTP `Link:` headers / sitemap `xhtml:link` when present):
1. **Reciprocity** — if page A declares an alternate B, B must declare A back. One-way hreflang is ignored by Google.
2. **BCP-47 validity** — each `hreflang` value is a valid language (`en`) or language-region (`en-GB`, `pt-BR`) tag; region is ISO-3166-1 alpha-2, not a country-of-language guess (`en-UK` is invalid; use `en-GB`).
3. **Self-reference** — the page lists itself in its own hreflang set.
4. **x-default** — at least one `hreflang="x-default"` for the language-selector / fallback URL.View the complete source on GitHubseo-local
When your agent uses it
Audit and generate Local SEO signals for local-business pages — verify LocalBusiness JSON-LD, NAP consistency, geo coordinates, openingHoursSpecification, and service-area, advise on Google Business Profile alignment, and produce ready-to-inject LocalBusiness JSON-LD. Module M19 (conditional). Feeds both the Search SEO and AI Visibility scores.
What it loads into context
Frontmatter
90 tokensAlways available to the agent
SKILL.md
1,320 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/validate-jsonld.mjs../../scripts/validate-jsonld.mjs../../scripts/validate-jsonld.mjs
Files
../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/validate-jsonld.mjsruns6,725 bytes../../scripts/validate-jsonld.mjsruns6,725 bytes../../scripts/validate-jsonld.mjsruns6,725 bytesSKILL.mdcore5,474 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-local (M19)
Local pages live and die on consistent, machine-readable place data. **Conditional module**: only runs when the page is classified local-business (storefront, service-area, multi-location, contact/location page). Reference: `references/schema-tier1.md` (LocalBusiness row).
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`jsonld[]`, `text_sample`, `iframes` for maps) and `vertical` from the envelope; Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`), only for local-business pages:
1. **LocalBusiness schema**: a `LocalBusiness` (or correct subtype, e.g. `Restaurant`, `Dentist`) JSON-LD block with required props — `name`, `address` (`PostalAddress`), `telephone`.
2. **NAP consistency**: visible Name / Address / Phone match across the page (header, footer, contact block) and match the schema values — no formatting drift, no stale numbers.
3. **Geo coordinates**: `geo` (`GeoCoordinates` with `latitude`/`longitude`) present and plausible for the stated address.
4. **Opening hours**: `openingHoursSpecification` present, structured (`dayOfWeek`, `opens`, `closes`), and agreeing with any visible hours.View the complete source on GitHubseo-meta-onpage
When your agent uses it
Audit and generate the document head — title and meta description length against the shared bands (title pass 30-60 chars, recommended 50-60; description pass 70-160, recommended 150-160), viewport, charset, <html lang>, and robots-meta sanity — flagging missing/duplicate/out-of-band values and producing length-bounded, keyword-aware replacements. Module M7. Feeds the Search SEO score.
What it loads into context
Frontmatter
110 tokensAlways available to the agent
SKILL.md
1,872 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/lib/bands.mjs../../scripts/parse-html.mjs../../scripts/parse-html.mjs../../scripts/parse-html.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/lib/bands.mjsruns1,942 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore7,494 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-meta-onpage (M7)
Head hygiene is cheap, deterministic, and the first thing every crawler reads. Reference: `references/schema-tier1.md` for the structured-data layer that sits alongside the head (owned by M5).
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`title`, `metas[]`, `robots_meta[]`, `canonicals[]`, `word_count`) plus `headers` (`content-language`, `x-robots-tag`); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Title** — exactly one `<title>`. Length is judged against the **shared bands** in `scripts/lib/bands.mjs` (the single source; `parse-html.mjs`, the on-page checks and this skill all read it): **pass 30-60 characters**, **recommended 50-60**. Inside 50-60 the verdict is `recommended`; inside 30-60 but outside that, `pass`; below 30 `short`, above 60 `long` — both are `warn`, never `fail`. **Fail** only when the title is missing or empty, or duplicated site-wide across distinct URLs. Not all caps; not a pure keyword list.
2. **Meta description** — present, judged against the same shared bands: **pass 70-160 characters**, **recommended 150-160**. Outside the pass band is `warn`, never `fail` — Google rewrites descriptions freely. Not duplicated site-wide; describes this page, not boilerplate.
3. **Viewport** — `<meta name="viewport" content="width=device-width, initial-scale=1">` present (fail if missing — mobile rendering/indexing depends on it).
4. **Charset** — `<meta charset="utf-8">` present and in the first 1024 bytes of `<head>`.View the complete source on GitHubseo-mobile
When your agent uses it
Audit mobile-friendliness of a page from the PageSnapshot — verify the viewport meta, no horizontal-scroll/fixed-width layouts, adequate tap targets, legible base font, and mobile/desktop content parity — and generate the viewport meta when missing. Module M7b. Feeds the Search SEO score.
What it loads into context
Frontmatter
83 tokensAlways available to the agent
SKILL.md
1,270 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/parse-html.mjs../../scripts/parse-html.mjs../../scripts/parse-html.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/parse-html.mjsruns7,444 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/parse-html.mjsruns7,444 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore5,393 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-mobile (M7b)
Google indexes mobile-first: the mobile rendering of a page is the canonical one for ranking. This module checks that a page is usable on a small viewport. Layout problems that hurt mobile usability often surface as CLS — cross-check `references/cwv-thresholds.md`.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`metas[]` for the viewport tag) and `parsed_rendered` when `render.used` is not `none` (layout/geometry checks need it); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Viewport meta**: a `<meta name="viewport">` exists with `width=device-width` and `initial-scale=1`. Flag missing, fixed-width (`width=1024`), or zoom-blocking (`user-scalable=no`, `maximum-scale=1`) values — the last also harms accessibility.
2. **No horizontal scroll**: detect fixed-width containers / large absolute widths that force the viewport wider than the device (content overflowing a ~360 px small viewport).
3. **Tap-target size**: interactive elements (links, buttons, form controls) should be large enough and spaced so adjacent targets are not easily mis-tapped.
4. **Legible base font**: body text rendered at a readable base size, not a desktop-only small size that forces pinch-zoom on mobile.View the complete source on GitHubseo-orchestrator
When your agent uses it
Orchestrates a full SEO + AI-search audit — acquires the site with the bundled scripts (crawl/snapshot), detects platform and vertical, runs the deterministic checks, dispatches the read-only specialist subagents in parallel with a dispatch envelope, merges their findings into a persisted report, and presents the two scores. Invoked by the `audit` and `geo` commands; not called directly.
What it loads into context
Frontmatter
150 tokensAlways available to the agent
SKILL.md
2,080 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- 1. Detect — one call
- 2. Dispatch (parallel — one message, four `Agent` calls)
- 3. Synthesize
- Degraded mode
Scripts executed separately
../../scripts/audit.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs../../scripts/report.mjs
Files
../../scripts/audit.mjsruns23,375 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytes../../scripts/report.mjsruns50,977 bytesSKILL.mdcore7,877 bytesSKILL.mdcore7,877 bytesSKILL.mdcore7,877 bytesSKILL.mdcore7,877 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-orchestrator (Layer 2)
Coordinates the whole audit in three phases: **detect → dispatch → synthesize**. The scripts are the acquisition layer; the model only judges. Read the compact stdout summaries and the JSON files they name — **never paste HTML into the conversation**. Nothing is written to the user's project; every artifact lives under `${CLAUDE_PLUGIN_DATA}/runs/<host>/<run-id>/`.
## 1. Detect — one call
1. Resolve the target and the flags forwarded by the command (`--pages`, `--max`, `--render`, `--ua`, `--vertical`, `--environment`, `--feed`, `--out`; default `--out "${CLAUDE_PLUGIN_DATA}/runs"`).
2. Run the whole deterministic pipeline **in one command**. `audit.mjs` acquires the target (it calls `crawl.mjs` for a URL, `snapshot.mjs` for `--pages 1` or a local path, and skips acquisition entirely when handed a run directory), writes `profile.json` (platform / framework / cms plugins / hosting / environment / capabilities / write_targets / `vertical_hints`, plus a vertical guess), runs the checks registry, and calls `report.mjs` for a first deterministic-only report:
```bash
node "${CLAUDE_PLUGIN_ROOT}/scripts/audit.mjs" <url|path> \
--out "${CLAUDE_PLUGIN_DATA}/runs" --checks deterministic --format json \
[--pages N] [--max N] [--render static|auto|js] [--ua <preset>] \
[--vertical <ids>] [--environment production|preview|staging|local] [--feed <path>]
```View the complete source on GitHubseo-platform-detect
When your agent uses it
Build the SiteProfile for an audited site — platform, framework, CMS plugins, hosting, environment, capabilities and write targets — by running scripts/detect-platform.mjs over the persisted PageSnapshot, then pick the knowledge cards each subagent needs. Support skill for seo-orchestrator; it merges this output with seo-vertical-detect into <run_dir>/profile.json.
What it loads into context
Frontmatter
108 tokensAlways available to the agent
SKILL.md
1,600 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- When to run
- How to run
- What comes back
- Merging into profile.json
- Which card goes to which subagent
- Honesty
Scripts executed separately
../../scripts/detect-platform.mjs
Files
../../scripts/detect-platform.mjsruns31,564 bytesSKILL.mdcore6,480 bytesSKILL.mdcore6,480 bytesSKILL.mdcore6,480 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-platform-detect
The profile decides three things no module can decide on its own: which platform-conditional check
ids fire, which findings are `not_applicable` because the platform owns the thing, and which fix
adapter `fix` will reach for. Run it once per audit, right after the snapshot, before dispatch.
Detection is orthogonal to the vertical: `seo-vertical-detect` answers *what kind of site is this*,
this skill answers *what is it built on*. Both feed one `profile.json`.
## When to run
In the normal `/audit` and `/geo` flow you do **not** run this yourself: `audit.mjs` already calls the
same `detect()` over the run's homepage snapshot and persists the result as `<run_dir>/profile.json`.View the complete source on GitHubseo-rendering
When your agent uses it
Audit a page's rendering strategy and JavaScript dependency — diff raw HTML against the rendered DOM to measure how much primary content, links, headings, and JSON-LD exist only after JS executes, classify CSR-only / SSR / SSG / ISR, and emit framework-specific guidance. Module M4. Feeds both the Search SEO and AI Visibility scores.
What it loads into context
Frontmatter
93 tokensAlways available to the agent
SKILL.md
1,561 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes (fixable: advisory)
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore6,630 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-rendering (M4)
Content that only exists after hydration is invisible to any consumer that does not run JavaScript. Google renders JS (documented); most third-party AI crawlers are **reported** not to (directional — see Honesty), so a CSR-only shell is primarily an AI-visibility and crawl-efficiency problem. JS dependency also delays first paint; see `references/cwv-thresholds.md` for how this feeds LCP/INP.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` and `parsed_rendered`, plus the `render{needed,signals,used,confidence,hint,delta}` block — the script already computed the raw-vs-rendered delta; `raw_html_path`/`rendered_html_path` name the files on disk; Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`) and the script-computed `render.delta`:
1. **Content delta**: diff `raw_html_path` vs `rendered_html_path` — measure the share of primary text, internal links, headings (h1–h3), and `<script type="application/ld+json">` blocks that appear **only** after JavaScript runs.
2. **Classify the strategy**: CSR-only (empty/near-empty raw shell, content injected client-side) / SSR / SSG (prerendered static) / ISR (prerendered + revalidated). Use raw-HTML completeness and framework signals (`__NEXT_DATA__`, hydration markers, `data-reactroot`, build manifests).
3. **Hydration-blocked text**: primary content present in raw HTML but hidden/empty until hydration, or rendered only into a client-only island.
4. **Lazy-loaded main content**: above-the-fold or primary content that requires scroll/intersection/interaction to load — invisible to a non-interacting crawler.View the complete source on GitHubseo-schema-jsonld
When your agent uses it
Audit and generate structured data (JSON-LD) for a page — detect, validate, and complete Tier-1 schema.org types (Article, Organization, Person, Product/Offer, BreadcrumbList, LocalBusiness, Review, VideoObject, Event), flag microdata/RDFa and deprecated-for-rich-results types (FAQPage/HowTo), and produce ready-to-inject JSON-LD blocks. Module M5. Feeds both the Search SEO and AI Visibility scores.
What it loads into context
Frontmatter
118 tokensAlways available to the agent
SKILL.md
1,669 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes (fixable: auto)
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/validate-jsonld.mjs../../scripts/validate-jsonld.mjs../../scripts/validate-jsonld.mjs
Files
../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/validate-jsonld.mjsruns6,725 bytes../../scripts/validate-jsonld.mjsruns6,725 bytes../../scripts/validate-jsonld.mjsruns6,725 bytesSKILL.mdcore6,668 bytesSKILL.mdcore6,668 bytesSKILL.mdcore6,668 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-schema-jsonld (M5)
Structured data is the single highest-leverage signal for both classic rich results and AI citation. Reference: `references/schema-tier1.md`. Templates: `schema/jsonld-templates/`.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`jsonld[]`, `microdata_items`, `headings[]`, `images[]`); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **Detect** every `<script type="application/ld+json">` block; parse JSON; note any inline microdata/RDFa (flag for migration to JSON-LD).
2. **Validate** each block: valid JSON, recognized `@type`, required + recommended properties present per `references/schema-tier1.md`.
3. **Completeness** vs the page's content & vertical: e.g. an article page should have `Article`/`BlogPosting` with `author` (Person), `datePublished`, `dateModified`, `image`, `publisher`; a product page `Product` + `Offer` (`price`, `priceCurrency`, `availability`).
4. **Entity hygiene**: stable `@id`, `@graph` linkage, `sameAs` (defer the sameAs audit detail to M6/seo-entity-linking).View the complete source on GitHubseo-score
When your agent uses it
Compute the two never-blended 0-100 scores (Search SEO and AI Visibility / GEO-AEO) from a set of findings, with severity-weighted category values, dynamic re-normalization of conditional modules, severity gating, and letter bands. Used by seo-orchestrator and the `score` command.
What it loads into context
Frontmatter
83 tokensAlways available to the agent
SKILL.md
798 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Steps
- Determinism
- Output
Scripts executed separately
../../scripts/checks/_shared.mjs../../scripts/score.mjs../../scripts/score.mjs
Files
../../scripts/checks/_shared.mjsruns17,733 bytes../../scripts/score.mjsruns43,922 bytes../../scripts/score.mjsruns43,922 bytesSKILL.mdcore3,036 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-score
Turns findings (conforming to `schema/finding.schema.json`) into the two scores. Full model in `references/scoring-model.md` — follow it exactly.
## Steps
1. Group findings by the category each module maps to, per score. A finding contributes only to the score(s) in `expected_impact.axis` (`search`, `ai`, or `both`).
2. **Category value** = `100 × Σ(status_factor × severity for scored findings) / Σ(severity)`, where `status_factor`: pass 1.0, warn 0.5, fail 0.0. Exclude `needs_api` and `not_applicable` from both sums.
3. **Active weights**: drop conditional categories (e-commerce/local/international) whose modules produced no findings; re-normalize remaining weights to sum to their active total.
4. **Score** = `Σ(category_value × weight) / Σ(active weight)` for each of Search SEO and AI Visibility.
5. **Severity gating**: if any finding has `severity: 5` and `status: fail`, cap the affected score at 40 and set `capped: true`.
6. Assign bands (A≥90, B≥80, C≥70, D≥60, F<60) and a one-line interpretation from the Search×AI quadrant.
6b. **Coverage floor**: report `coverage` (the % of the axis's always-on weight that carried a scored finding). Below 50% the axis comes back `provisional: true` with `state: "partial"` — quote the band *and* the coverage figure together, never the letter on its own.
7. M21 (AI discovery & agent endpoints — llms.txt, agents.md, UCP, agentic sitemap) weight is 0 — report it, never let it move the AI score.View the complete source on GitHubseo-sitemaps
When your agent uses it
Audit and generate sitemaps and discovery files — validate XML sitemap presence/size/extensions/lastmod, check robots.txt referencing and sitemap-to-canonical consistency, reconcile orphans against the link graph, and produce repaired sitemap entries plus a robots.txt Sitemap line. Module M17. Feeds the Search SEO score.
What it loads into context
Frontmatter
86 tokensAlways available to the agent
SKILL.md
1,773 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/parse-robots-sitemap.mjs../../scripts/parse-robots-sitemap.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/parse-robots-sitemap.mjsruns11,144 bytes../../scripts/parse-robots-sitemap.mjsruns11,144 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore7,142 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-sitemaps (M17)
Sitemaps are the discovery contract you hand the crawler — they should list exactly the canonical, indexable URLs and nothing else. Schema rules for related markup: `references/schema-tier1.md`.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` plus `<run_dir>/site/sitemaps.json` (+ `site/sitemap-urls.txt`), `<run_dir>/site/robots.json`, and `<run_dir>/crawl.json` for orphan reconciliation and per-URL status; Grep `pages/<slug>.html` for verbatim evidence. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`) plus `site/sitemaps.json` and `site/robots.json`:
1. **Presence & validity**: locate XML sitemap(s) (`/sitemap.xml`, robots `Sitemap:` lines, sitemap index); parse as well-formed XML against the sitemaps.org schema.
2. **Size limits**: each sitemap `<=50,000` URLs and `<=50MB` uncompressed; if exceeded, expect a sitemap index splitting the set.
3. **Extensions**: where relevant, validate `image:`, `video:`, and `news:` namespace entries (correct namespace declared, required child elements present).
4. **lastmod accuracy**: `<lastmod>` is valid ISO 8601 and reflects real last-modified time — not a build-time stamp on every URL (which trains crawlers to ignore it).View the complete source on GitHubseo-social-cards
When your agent uses it
Audit and generate Open Graph and Twitter/X social-card tags for a page — check og:title/description/url/type, og:image presence/size/reachability, and twitter:card/title/description/image, then inject complete card tags inferred from the page. Module M8. Feeds the Search SEO score.
What it loads into context
Frontmatter
85 tokensAlways available to the agent
SKILL.md
1,113 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Audits
- Fixes
- Verification
- Findings
- Honesty
Scripts executed separately
../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs../../scripts/snapshot.mjs
Files
../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytes../../scripts/snapshot.mjsruns35,385 bytesSKILL.mdcore4,328 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-social-cards (M8)
Social cards control how a URL renders when shared on social, chat, and AI assistants — a CTR/sharing signal, not a ranking one (see Honesty). Open Graph metadata is the same `<head>`-level hygiene tier as the structured-data work in `references/schema-tier1.md`, so audit it alongside that module.
## Inputs
Work from the PageSnapshot named in your dispatch envelope: read `parsed` from `<run_dir>/pages/<slug>.json` (`metas[]` for `og:*`/`twitter:*`, `canonicals[]`, `images[]`); Grep `pages/<slug>.html` for verbatim evidence; site artifacts live in `<run_dir>/site/{robots.json,sitemaps.json,discovery.json}`. Deterministic findings already emitted by `audit.mjs` are listed in `<run_dir>/findings.deterministic.json` — do not re-emit those ids; add model-judged findings only. If invoked directly with a URL/path and no snapshot exists, first run `node "${CLAUDE_PLUGIN_ROOT}/scripts/snapshot.mjs" <target> --out "${CLAUDE_PLUGIN_DATA}/runs"` and use the printed snapshot path.
## Audits
Working from the PageSnapshot (`parsed_rendered` when `render.used` is not `none`, else `parsed`):
1. **OG content**: `og:title`, `og:description`, `og:url`, `og:type` present and non-empty; `og:url` matches the canonical; `og:type` sane for the page (e.g. `article` vs `website`).
2. **OG image**: `og:image` present, ~1200x630 (1.91:1) and >=200x200, and **reachable** (HTTP 200, image content-type, absolute URL).
3. **Twitter/X**: `twitter:card` (prefer `summary_large_image`); `twitter:title`, `twitter:description`, `twitter:image`. Twitter falls back to OG when these are absent — note when it is relying on OG.
4. **Fallback behavior**: when OG is absent entirely, record what crawlers would derive (`<title>`, meta description, first content `<img>`) so the finding shows the actual degraded card, not a guess.View the complete source on GitHubseo-vertical-detect
When your agent uses it
Classify a website as ecommerce, local-business, blog-publisher, saas, docs, or generic (multiple may apply), and report detected locales, from the persisted PageSnapshot plus the platform detector's vertical hints. Used by seo-orchestrator to decide which conditional modules to run and how to reweight the scores.
What it loads into context
Frontmatter
91 tokensAlways available to the agent
SKILL.md
647 tokensWhen the skill is activated
Reference documents
0 tokensLoaded on request
Token counts are estimates. Scripts run separately; they are not counted as reference context.
The playbook
- Inputs
- Method
- Output
Files
SKILL.mdcore2,531 bytes
Read SKILL.md
<!-- Portions Copyright (c) 2026 Enrique Rocha (@soyenriquerocha) - see LICENSE for original authors · tododeia.com · id er-d03758e1 -->
# seo-vertical-detect
Given the PageSnapshot, classify the site so the orchestrator can route conditional modules. Read `references/routing.md` for the signal table.
## Inputs
Read-only over persisted data — no fetching: the homepage `parsed` (`jsonld[]`, `anchors[]`, `metas[]`, `hreflang[]`, `<html lang>`, `text_sample`) from `<run_dir>/pages/<slug>.json`, `<run_dir>/crawl.json` (route patterns, templates), `<run_dir>/site/discovery.json` (llms.txt presence), and the `vertical_hints[]` emitted by `detect-platform.mjs` in `<run_dir>/profile.json` (e.g. WooCommerce/Shopify → ecommerce; a docs generator → docs). Hints are signals, not verdicts — cite them in `signals` like any other evidence.
## Method
Score each vertical from observable signals (not guesses):
- **ecommerce** — `Product`/`Offer` JSON-LD, cart/checkout/add-to-cart routes or buttons, price + currency elements, `og:type=product`.
- **local-business** — `LocalBusiness` JSON-LD, NAP block (name/address/phone), maps embed, opening hours, store-locator.
- **blog-publisher** — `Article`/`BlogPosting` JSON-LD, author bylines/author pages, RSS/Atom `<link>`, dated post archives.
- **saas** — pricing/signup/login routes, `SoftwareApplication` schema, feature/integration/docs pages.View the complete source on GitHubSkills included
- audit
- compare
- fix
- geo
- score
- seo-agent-readiness
- seo-ai-crawlers
- seo-ai-discovery
- seo-core-web-vitals
- seo-crawl-render
- seo-crawlability
- seo-ecommerce
- seo-eeat
- seo-entity-linking
- seo-fix-apply
- seo-freshness
- seo-geo-answerblocks
- seo-geo-factdensity
- seo-headings-structure
- seo-images-media
- seo-indexability
- seo-internal-linking
- seo-international
- seo-local
- seo-meta-onpage
- seo-mobile
- seo-orchestrator
- seo-platform-detect
- seo-rendering
- seo-schema-jsonld
- seo-score
- seo-sitemaps
- seo-social-cards
- seo-vertical-detect
Repository facts
- License
- MIT
- Created
- Jun 1, 2026
- Last pushed
- Oct 25, 2026
- Forks
- 12
- Open issues
- 0
- Project activity
- Active
Before you install
Network or broad shellAutomated signals, not a guarantee
Checked Nov 6, 2026
Not fully analyzed: some files were too long, unreadable or missing. This never lowers the level.
Show 253 notes
hooks/hooks.json:1Analysis incomplete: Referenced executable target unavailable; not analyzedhooks/hooks.json:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/audit.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/audit.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/audit.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/compare.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/snapshot.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/snapshot.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/snapshot.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/snapshot.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/snapshot.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedskills/compare/SKILL.md:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/fix-plan.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/fix-plan.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/fix-plan.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/fix-plan.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/fix-plan.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/fix-plan.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/fix-plan.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzedscripts/fix-ticket.mjs:1Analysis incomplete: Referenced executable target unavailable; not analyzed
- 32 broad permissionsin 32 files: audit/SKILL.md:1 compare/SKILL.md:1 fix/SKILL.md:1 and 29 more
- 1 dangerous shell commandin 1 file: scripts/guard-bash.mjs:61
- 125 network referencesin 22 files: scripts/ai-discovery.mjs:8 scripts/parse-robots-sitemap.mjs:6 scripts/parse-html.mjs:6 and 19 more
- 5 obfuscation signalsin 2 files: scripts/acp-feed-lint.mjs:40 scripts/gsc-ai-import.mjs:39
- No instruction overrides
- 286 executable scriptsin 28 files: scripts/snapshot.mjs:1 scripts/ai-discovery.mjs:1 scripts/crawl.mjs:1 and 25 more
- 12 references to secretsin 5 files: scripts/audit.mjs:122 .claude-plugin/plugin.json:42 scripts/psi-client.mjs:11 and 2 more
- No requests to vote in Hot
Show all 461 signals by file
.claude-plugin/marketplace.json
.claude-plugin/plugin.json
- Network accessline 1
{ "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", - Network accessline 2
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json", - Network accessline 9
"email": "enriqueers98@gmail.com" }, "homepage": "https://github.com/Hainrixz/claude-seo-ai", - Network accessline 10
}, "homepage": "https://github.com/Hainrixz/claude-seo-ai", "repository": "https://github.com/Hainrixz/claude-seo-ai", - Network accessline 11
"homepage": "https://github.com/Hainrixz/claude-seo-ai", - Network accessline 12
"repository": "https://github.com/Hainrixz/claude-seo-ai", - Access to secretsline 42
[Credential-related source omitted] - Access to secretsline 83
[Credential-related source omitted] - Network accessline 95
"type": "string", "title": "Ghost site URL", "description": "https://blog.example.com — the Admin API lives under /ghost/api/admin/." - Network accessline 96
"title": "Ghost site URL", "description": "https://blog.example.com — the Admin API lives under /ghost/api/admin/." }, - Network accessline 97
"description": "https://blog.example.com — the Admin API lives under /ghost/api/admin/." - Access to secretsline 122
[Credential-related source omitted]
scripts/acp-feed-lint.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Possible obfuscationline 40
const src = String(input == null ? '' : input).replace(/^<U+FEFF>/, ''); - Possible obfuscationline 79
const lines = String(input == null ? '' : input).replace(/^<U+FEFF>/, '').split(/\r?\n/); - Possible obfuscationline 97
const first = String(input || '').replace(/^<U+FEFF>/, '').split(/\r?\n/).find((l) => l.trim());
scripts/agent-readiness.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 11
// verbatim in `not_checkable_static` instead of being silently skipped. // // Usage: node agent-readiness.mjs --url https://example.com/page - Network accessline 12
// // Usage: node agent-readiness.mjs --url https://example.com/page // node agent-readiness.mjs --file ./page.html [--url <page url>] [--m4 ./render.json] - Network accessline 13
// Usage: node agent-readiness.mjs --url https://example.com/page
scripts/ai-discovery.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 8
// are written for. Every finding built from this output is speculative or directional. // // Usage: node ai-discovery.mjs --url https://example.com (probes the origin) - Network accessline 8
// are written for. Every finding built from this output is speculative or directional. // // Usage: node ai-discovery.mjs --url https://example.com (probes the origin) - Network accessline 9
// // Usage: node ai-discovery.mjs --url https://example.com (probes the origin) // node ai-discovery.mjs --run-dir <run> (reads <run>/site/discovery.json) - Network accessline 9
// // Usage: node ai-discovery.mjs --url https://example.com (probes the origin) // node ai-discovery.mjs --run-dir <run> (reads <run>/site/discovery.json) - Network accessline 10
// Usage: node ai-discovery.mjs --url https://example.com (probes the origin) - Network accessline 10
// Usage: node ai-discovery.mjs --url https://example.com (probes the origin) - Network accessline 53
* - a broken `](` pair anywhere in the bullet, or * - a URL/`[` at the head of the bullet, or at the head of what follows a short `Label:` prefix * (`- Docs: https://…` is the classic un-bracketed link, not a sentence). - Network accessline 53
* - a broken `](` pair anywhere in the bullet, or * - a URL/`[` at the head of the bullet, or at the head of what follows a short `Label:` prefix * (`- Docs: https://…` is the classic un-bracketed link, not a sentence). - Network accessline 54
* - a URL/`[` at the head of the bullet, or at the head of what follows a short `Label:` prefix * (`- Docs: https://…` is the classic un-bracketed link, not a sentence). * A URL that merely appears inside a sentence — or inside a ba - Network accessline 54
* - a URL/`[` at the head of the bullet, or at the head of what follows a short `Label:` prefix * (`- Docs: https://…` is the classic un-bracketed link, not a sentence). * A URL that merely appears inside a sentence — or inside a ba - Network accessline 55
* (`- Docs: https://…` is the classic un-bracketed link, not a sentence). - Network accessline 55
* (`- Docs: https://…` is the classic un-bracketed link, not a sentence). - Network accessline 488
let origin; try { origin = new URL(urlFlag.trim()).origin; } catch { return { result: { error: 'invalid --url ' + urlFlag, hint: 'include the scheme, e.g. https://example.com' }, code: EXIT.USAGE }; } - Network accessline 488
let origin; try { origin = new URL(urlFlag.trim()).origin; } catch { return { result: { error: 'invalid --url ' + urlFlag, hint: 'include the scheme, e.g. https://example.com' }, code: EXIT.USAGE }; } - Network accessline 489
try { origin = new URL(urlFlag.trim()).origin; } catch { return { result: { error: 'invalid --url ' + urlFlag, hint: 'include the scheme, e.g. https://example.com' }, code: EXIT.USAGE }; } source = 'http'; - Network accessline 489
try { origin = new URL(urlFlag.trim()).origin; } catch { return { result: { error: 'invalid --url ' + urlFlag, hint: 'include the scheme, e.g. https://example.com' }, code: EXIT.USAGE }; } source = 'http'; - Network accessline 490
catch { return { result: { error: 'invalid --url ' + urlFlag, hint: 'include the scheme, e.g. https://example.com' }, code: EXIT.USAGE }; } - Network accessline 490
catch { return { result: { error: 'invalid --url ' + urlFlag, hint: 'include the scheme, e.g. https://example.com' }, code: EXIT.USAGE }; }
scripts/ai-eligibility.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 12
// verdict for Googlebot on this URL — and states what is unknown instead of guessing. // // Usage: node ai-eligibility.mjs --url https://example.com/page - Network accessline 13
// // Usage: node ai-eligibility.mjs --url https://example.com/page // node ai-eligibility.mjs --file ./page.html [--url <page url>] [--robots ./robots.txt] - Network accessline 14
// Usage: node ai-eligibility.mjs --url https://example.com/page - Network accessline 214
/** Locate a robots source: --robots <file>, the snapshot's run dir, or a live fetch (url mode). */
scripts/audit.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Access to secretsline 122
[Credential-related source omitted] - Access to secretsline 122
[Credential-related source omitted] - Access to secretsline 352
[Credential-related source omitted] - Access to secretsline 352
[Credential-related source omitted]
scripts/check-answerblocks.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Network accessline 6
// judgment stays a manual_review pass; this provides the reproducible counts. // // Usage: node check-answerblocks.mjs --url https://example.com/post [--lang en|es|auto] - Network accessline 7
// // Usage: node check-answerblocks.mjs --url https://example.com/post [--lang en|es|auto] // node check-answerblocks.mjs --file ./post.html [--url <page url>] [--lang en|es|auto] - Network accessline 8
// Usage: node check-answerblocks.mjs --url https://example.com/post [--lang en|es|auto]
scripts/check-freshness.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 6
// HTTP Last-Modified header, plus content age. Never used to backdate anything. // // Usage: node check-freshness.mjs --url https://example.com/post [--lang en|es|auto] - Network accessline 7
// // Usage: node check-freshness.mjs --url https://example.com/post [--lang en|es|auto] // node check-freshness.mjs --file ./post.html [--url <page url>] [--lang en|es|auto] - Network accessline 8
// Usage: node check-freshness.mjs --url https://example.com/post [--lang en|es|auto]
scripts/checks/_shared.mjs
scripts/compare.mjs
scripts/crawl.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 163
/** `<url>` | `--url <u>` | a bare host (`example.com` → `https://example.com/`). */
scripts/detect-platform.mjs
- Executable scriptline 1
#!/usr/bin/env node - Network accessline 12
// Usage: // node detect-platform.mjs --snapshot <run>/pages/<slug>.json [--path <project>] [--probe] // node detect-platform.mjs --url https://example.com [--path <project>] [--probe] - Network accessline 13
// node detect-platform.mjs --snapshot <run>/pages/<slug>.json [--path <project>] [--probe] // node detect-platform.mjs --url https://example.com [--path <project>] [--probe] // node detect-platform.mjs --path <project> - Network accessline 14
// node detect-platform.mjs --url https://example.com [--path <project>] [--probe]
scripts/factdensity.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 6
// (is a claim "strong"? is a source "authoritative"?) stay manual_review. // // Usage: node factdensity.mjs --url https://example.com/post [--lang en|es|auto] - Network accessline 7
// // Usage: node factdensity.mjs --url https://example.com/post [--lang en|es|auto] // node factdensity.mjs --file ./post.html [--url <page url>] [--lang en|es|auto] - Network accessline 8
// Usage: node factdensity.mjs --url https://example.com/post [--lang en|es|auto]
scripts/fix-plan.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable
scripts/fix-ticket.mjs
scripts/gsc-ai-import.mjs
scripts/guard-bash.mjs
- Executable scriptline 1
#!/usr/bin/env node - Access to secretsline 7
[Credential-related source omitted] - Dangerous shell operationline 61
const MAYBE = /(shopify|wp[\s.]|wp$|curl|wget|node\s+-{1,2}e|adapters[\\/]|\btee\b|>)/i; - Network accessline 66
* * A bare `&` is NOT a separator here. It is the character that joins query-string parameters, and * splitting on it tore `curl "https://shop.myshopify.com/admin/api/2026-07/x.json?a=1&b=2" -X POST` - Network accessline 67
* A bare `&` is NOT a separator here. It is the character that joins query-string parameters, and * splitting on it tore `curl "https://shop.myshopify.com/admin/api/2026-07/x.json?a=1&b=2" -X POST` * in half: the piece carrying the API ho - Network accessline 68
* splitting on it tore `curl "https://shop.myshopify.com/admin/api/2026-07/x.json?a=1&b=2" -X POST`
scripts/hreflang-check.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 5
// Audit hreflang annotations (HTML <link> tags ∪ HTTP Link header). Backs M20 (international). // // Usage: node hreflang-check.mjs --url https://example.com/page - Network accessline 6
// // Usage: node hreflang-check.mjs --url https://example.com/page // node hreflang-check.mjs --file ./page.html [--url <self>] - Network accessline 7
// Usage: node hreflang-check.mjs --url https://example.com/page - Network accessline 8
// node hreflang-check.mjs --file ./page.html [--url <self>] // node hreflang-check.mjs --url https://example.com/page --deep [--budget 25] [--ua <preset>] (fetch alternates, check reciprocity) // Exit codes: 0 ok · 1 usage - Network accessline 9
// node hreflang-check.mjs --url https://example.com/page --deep [--budget 25] [--ua <preset>] (fetch alternates, check reciprocity)
scripts/lib/bands.mjs
- Executable scriptline 1
Executable file
scripts/link-graph.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Network accessline 5
// Analyze internal linking. Backs M10 (internal linking & semantics). // // Usage: node link-graph.mjs --url https://example.com/page (single-page link profile) - Network accessline 6
// // Usage: node link-graph.mjs --url https://example.com/page (single-page link profile) // node link-graph.mjs --url https://example.com --crawl [--max 25] [--max-depth 3] [--no-robots] [--ua <preset>] - Network accessline 7
// Usage: node link-graph.mjs --url https://example.com/page (single-page link profile) - Network accessline 8
// node link-graph.mjs --url https://example.com --crawl [--max 25] [--max-depth 3] [--no-robots] [--ua <preset>] - Network accessline 9
// node link-graph.mjs --file ./page.html --base https://example.com
scripts/parse-html.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 6
// Backs verification for M7 (title/meta/headings), M8 (social), M9 (images), M10 (links). // // Usage: node parse-html.mjs --url https://example.com - Network accessline 6
// Backs verification for M7 (title/meta/headings), M8 (social), M9 (images), M10 (links). // // Usage: node parse-html.mjs --url https://example.com - Network accessline 6
// Backs verification for M7 (title/meta/headings), M8 (social), M9 (images), M10 (links). // // Usage: node parse-html.mjs --url https://example.com - Network accessline 6
// Backs verification for M7 (title/meta/headings), M8 (social), M9 (images), M10 (links). // // Usage: node parse-html.mjs --url https://example.com - Network accessline 7
// // Usage: node parse-html.mjs --url https://example.com // node parse-html.mjs --file ./index.html [--url https://example.com/page] (--url gives the page URL for canonical checks) - Network accessline 7
// // Usage: node parse-html.mjs --url https://example.com // node parse-html.mjs --file ./index.html [--url https://example.com/page] (--url gives the page URL for canonical checks) - Network accessline 7
// // Usage: node parse-html.mjs --url https://example.com // node parse-html.mjs --file ./index.html [--url https://example.com/page] (--url gives the page URL for canonical checks) - Network accessline 7
// // Usage: node parse-html.mjs --url https://example.com // node parse-html.mjs --file ./index.html [--url https://example.com/page] (--url gives the page URL for canonical checks) - Network accessline 8
// Usage: node parse-html.mjs --url https://example.com - Network accessline 8
// Usage: node parse-html.mjs --url https://example.com - Network accessline 8
// Usage: node parse-html.mjs --url https://example.com - Network accessline 8
// Usage: node parse-html.mjs --url https://example.com - Network accessline 9
// node parse-html.mjs --file ./index.html [--url https://example.com/page] (--url gives the page URL for canonical checks) - Network accessline 9
// node parse-html.mjs --file ./index.html [--url https://example.com/page] (--url gives the page URL for canonical checks) - Network accessline 9
// node parse-html.mjs --file ./index.html [--url https://example.com/page] (--url gives the page URL for canonical checks) - Network accessline 9
// node parse-html.mjs --file ./index.html [--url https://example.com/page] (--url gives the page URL for canonical checks)
scripts/parse-robots-sitemap.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Network accessline 6
// sitemap of a site. Backs M1 (crawlability), M14 (AI crawlers), M17 (sitemaps). // // Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) - Network accessline 6
// sitemap of a site. Backs M1 (crawlability), M14 (AI crawlers), M17 (sitemaps). // // Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) - Network accessline 6
// sitemap of a site. Backs M1 (crawlability), M14 (AI crawlers), M17 (sitemaps). // // Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) - Network accessline 7
// // Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) // node parse-robots-sitemap.mjs --url https://example.com --path /pricing (override the audited path - Network accessline 7
// // Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) // node parse-robots-sitemap.mjs --url https://example.com --path /pricing (override the audited path - Network accessline 7
// // Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) // node parse-robots-sitemap.mjs --url https://example.com --path /pricing (override the audited path - Network accessline 8
// Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) - Network accessline 8
// Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) - Network accessline 8
// Usage: node parse-robots-sitemap.mjs --url https://example.com/some/page (robots + verdicts for that path + all sitemaps) - Network accessline 9
// node parse-robots-sitemap.mjs --url https://example.com --path /pricing (override the audited path) - Network accessline 9
// node parse-robots-sitemap.mjs --url https://example.com --path /pricing (override the audited path) - Network accessline 9
// node parse-robots-sitemap.mjs --url https://example.com --path /pricing (override the audited path) - Network accessline 10
// node parse-robots-sitemap.mjs --robots https://example.com/robots.txt - Network accessline 10
// node parse-robots-sitemap.mjs --robots https://example.com/robots.txt - Network accessline 10
// node parse-robots-sitemap.mjs --robots https://example.com/robots.txt - Network accessline 11
// node parse-robots-sitemap.mjs --sitemap https://example.com/sitemap.xml - Network accessline 11
// node parse-robots-sitemap.mjs --sitemap https://example.com/sitemap.xml - Network accessline 11
// node parse-robots-sitemap.mjs --sitemap https://example.com/sitemap.xml
scripts/probe-report.mjs
- Executable scriptline 1
#!/usr/bin/env node
scripts/psi-client.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Network accessline 6
// Field (CrUX, p75) data is what Google ranks on; lab data is labeled separately. // // Usage: node psi-client.mjs --url https://example.com [--strategy mobile|desktop] [--timeout 60000] - Network accessline 7
// // Usage: node psi-client.mjs --url https://example.com [--strategy mobile|desktop] [--timeout 60000] // (a key is optional but recommended; without network/key the result is status:"needs_api") - Network accessline 8
// Usage: node psi-client.mjs --url https://example.com [--strategy mobile|desktop] [--timeout 60000] - Access to secretsline 11
[Credential-related source omitted] - Access to secretsline 59
[Credential-related source omitted] - Network accessline 61
const api = new URL('https://www.googleapis.com/pagespeedonline/v5/runPagespeed'); - Access to secretsline 76
[Credential-related source omitted]
scripts/report.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable
scripts/score.mjs
scripts/snapshot.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable
scripts/ua-diff.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Network accessline 10
// UA can be refused for legitimate reasons, so every finding built on this output is directional. // // Usage: node ua-diff.mjs --url https://example.com/page - Network accessline 10
// UA can be refused for legitimate reasons, so every finding built on this output is directional. // // Usage: node ua-diff.mjs --url https://example.com/page - Network accessline 11
// // Usage: node ua-diff.mjs --url https://example.com/page // node ua-diff.mjs --url <u> --ua default,googlebot,gptbot [--run-dir <run>] [--timeout 20000] - Network accessline 11
// // Usage: node ua-diff.mjs --url https://example.com/page // node ua-diff.mjs --url <u> --ua default,googlebot,gptbot [--run-dir <run>] [--timeout 20000] - Network accessline 12
// Usage: node ua-diff.mjs --url https://example.com/page - Network accessline 12
// Usage: node ua-diff.mjs --url https://example.com/page
scripts/validate-jsonld.mjs
- Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
#!/usr/bin/env node - Executable scriptline 1
Executable file; content unavailable - Executable scriptline 1
Executable file; content unavailable - Network accessline 6
// M18 (Product), M19 (LocalBusiness), M13 (dates). // // Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] - Network accessline 6
// M18 (Product), M19 (LocalBusiness), M13 (dates). // // Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] - Network accessline 6
// M18 (Product), M19 (LocalBusiness), M13 (dates). // // Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] - Network accessline 6
// M18 (Product), M19 (LocalBusiness), M13 (dates). // // Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] - Network accessline 7
// // Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] // node validate-jsonld.mjs --file ./page.html - Network accessline 7
// // Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] // node validate-jsonld.mjs --file ./page.html - Network accessline 7
// // Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] // node validate-jsonld.mjs --file ./page.html - Network accessline 7
// // Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] // node validate-jsonld.mjs --file ./page.html - Network accessline 8
// Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] - Network accessline 8
// Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] - Network accessline 8
// Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product] - Network accessline 8
// Usage: node validate-jsonld.mjs --url https://example.com [--type Article] [--type Product]
skills/audit/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash, Agent - Network accessline 13
`$ARGUMENTS` = `<url|path> [flags]`. The target is a URL (`https://…`) or a local web project / built HTML. If no target is given, ask for one. Flags are forwarded verbatim to the orchestrator, which passes them to `audit.mjs`: `--pages N`
skills/compare/SKILL.md
- Broad permissionsline 1
Read, Glob, Bash(node "${CLAUDE_PLUGIN_ROOT}/scripts/*"), WebSearch
skills/fix/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, Bash, Agent - Network accessline 135
framework site gets its per-resource fixes through the platform adapter and everything else through `instructions`, plus an offer to re-run with `--project <dir>` for source edits. - **Local framework project**: the audit may have run aga - Network accessline 136
through `instructions`, plus an offer to re-run with `--project <dir>` for source edits. - **Local framework project**: the audit may have run against `http://localhost:3000` or built output, while the fix edits **source** through `route- - Network accessline 137
- **Local framework project**: the audit may have run against `http://localhost:3000` or built
skills/geo/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, WebSearch, Bash, Agent - Network accessline 26
{ "host": "example.com", "queries": [ { "query": "best sustainable running shoes", "results": [ { "rank": 1, "url": "https://…", "title": "…" } ] } ] } - Network accessline 28
"results": [ { "rank": 1, "url": "https://…", "title": "…" } ] } ] }
skills/score/SKILL.md
- Broad permissionsline 1
Read, Bash
skills/seo-agent-readiness/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, Bash
skills/seo-ai-crawlers/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-ai-discovery/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, Bash
skills/seo-core-web-vitals/SKILL.md
skills/seo-crawl-render/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, Bash - Network accessline 36
Site artifacts are built once per run: `site/robots.json` (parsed REP + per-bot verdicts), `site/sitemaps.json` (index BFS, gzip, lastmod stats) + `site/sitemap-urls.txt`, `site/discovery.json` (probes for `/llms.txt`, `/llms-full.txt`, `/a
skills/seo-crawlability/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-ecommerce/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-eeat/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-entity-linking/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-freshness/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-geo-answerblocks/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-geo-factdensity/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-headings-structure/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-images-media/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-indexability/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-internal-linking/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-international/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-local/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-meta-onpage/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-mobile/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-orchestrator/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash(node "${CLAUDE_PLUGIN_ROOT}/scripts/*"), Bash(mkdir -p "${CLAUDE_PLUGIN_DATA}/runs/*"), Bash(cat > "${CLAUDE_PLUGIN_DATA}/runs/*"), Agent
skills/seo-platform-detect/SKILL.md
- Broad permissionsline 1
Read, Bash(node "${CLAUDE_PLUGIN_ROOT}/scripts/*")
skills/seo-rendering/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-schema-jsonld/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-score/SKILL.md
- Broad permissionsline 1
Read, Bash
skills/seo-sitemaps/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
skills/seo-social-cards/SKILL.md
- Broad permissionsline 1
Read, Grep, Glob, WebFetch, Bash
How the signals fit together
Facts from a static read of the files at the pinned commit. They describe what the code does, not why.
Some files could not be read; the facts below still hold.
Declared and executed: 2 facts
- Contacts www.googleapis.com, which SKILL.md, the README and the plugin manifest do not mentionscripts/psi-client.mjs:61High confidence
- Allows Bash without restrictions, though it has no scripts or shell blocksaudit/SKILL.md:5High confidence
Signal confidence: 447 facts
- Executable script in code of a file that runsscripts/acp-feed-lint.mjs:1High confidence
- Executable script in code of a file that runsscripts/acp-feed-lint.mjs:1High confidence
- Possible obfuscation in code of a file that runsscripts/acp-feed-lint.mjs:40High confidence
- Possible obfuscation in code of a file that runsscripts/acp-feed-lint.mjs:79High confidence
- Possible obfuscation in code of a file that runsscripts/acp-feed-lint.mjs:97High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Executable script in code of a file that runsscripts/agent-readiness.mjs:1High confidence
- Network access in a comment of a file that runsscripts/agent-readiness.mjs:11Low confidence
- Network access in a comment of a file that runsscripts/agent-readiness.mjs:12Low confidence
- Network access in a comment of a file that runsscripts/agent-readiness.mjs:13Low confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-discovery.mjs:1High confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:8Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:8Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:9Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:9Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:10Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:10Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:53Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:53Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:54Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:54Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:55Low confidence
- Network access in a comment of a file that runsscripts/ai-discovery.mjs:55Low confidence
- Network access in code of a file that runsscripts/ai-discovery.mjs:488High confidence
- Network access in code of a file that runsscripts/ai-discovery.mjs:488High confidence
- Network access in code of a file that runsscripts/ai-discovery.mjs:489High confidence
- Network access in code of a file that runsscripts/ai-discovery.mjs:489High confidence
- Network access in code of a file that runsscripts/ai-discovery.mjs:490High confidence
- Network access in code of a file that runsscripts/ai-discovery.mjs:490High confidence
- Executable script in code of a file that runsscripts/ai-eligibility.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-eligibility.mjs:1High confidence
- Executable script in code of a file that runsscripts/ai-eligibility.mjs:1High confidence
and 397 more
Works with
| Agent | Status | Evidence | Install to |
|---|---|---|---|
| Claude Code | Native | Manifest .claude-plugin/marketplace.json | .claude/skills/<skill-name>/ |
| Codex | Compatible | Standard SKILL.md skills/audit/SKILL.md | .agents/skills/<skill-name>/ |
| Cursor | Compatible | Standard SKILL.md skills/audit/SKILL.md | .cursor/skills/<skill-name>/ |
| Gemini CLI | Compatible | Standard SKILL.md skills/audit/SKILL.md | .gemini/skills/<skill-name>/ |
| GitHub Copilot | Compatible | Standard SKILL.md skills/audit/SKILL.md | .github/skills/<skill-name>/ |
| OpenCode | Compatible | Standard SKILL.md skills/audit/SKILL.md | .opencode/skills/<skill-name>/ |
- Native The repository ships a manifest, folder or frontmatter field for this agent.
- Compatible A standard SKILL.md that this agent can read.
- Unknown No evidence either way.
Derived automatically from paths, manifests and frontmatter. Not tested on each agent.