All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
- Keywords Everywhere (Open PageRank) as an optional, free-signup backlinks
fallback source: a single 0-10 domain rank metric used for the Profile
Overview section when Moz isn't configured. Wired through
backlinks_auth.py(newkeywordseverywhereservice) and a newkeywordseverywhere_api.pyclient, following the existing Moz/Bing auth and source patterns. The outbound call goes through the sharedurl_safety.safe_requests_getDNS-pinned helper, domains are normalized and SSRF-checked before use, and requests are capped at 100 domains per call.keywordseverywhere_api.pyis registered inruntime.py'sALLOWED_CORE_SCRIPTS(a script invoked from a SKILL.md but missing from that allowlist is refused byclaude-seo run; a new test intests/test_runtime.pyguards against that class of bug for every script every SKILL.md/agent invokes). The live API path is unverified: landing this required no Keywords Everywhere account, and none was available to exercise the real endpoint end to end (#262).
- The five judgment-heavy agents (
seo-content,seo-geo,seo-sxo,seo-cluster,seo-drift) declaremodel: opus; the other thirteen stay on Sonnet. README documents the cost implication and how to override per agent (#268). - Applied the mechanical hunks from #196 ("ponytail cleanup") by hand: hoisted
three function-local
from urllib.parse import urlparseimports to the top ofvalidate_backlink_report.py, and deletedcommoncrawl_graph.py's dead_stream_gz_lineshelper (zero callers) along with thegzip/ioimports it alone used. Left out thehashlib.file_digestrewrite (Python 3.11+ only;pyproject.tomlrequires >=3.10) and narrowingdataforseo_normalize.py's--modulechoices, a compatibility change rather than a cleanup. Credit: pookNast (#196).
metadata_template.py, added in v2.3.0, was not registered in the launcher'sALLOWED_CORE_SCRIPTS, soclaude-seo run metadata_template.pywas refused for/seo pageand/seo programmatic. Registered, with a test that every script an instruction file invokes is allowlisted.- The dataforseo, ahrefs and firecrawl PowerShell installers created
mcpServersas a hashtable, whichConvertTo-Jsonserialised as{}when~/.claude.jsonwas missing or had nomcpServersyet, silently dropping the server entry; they now create an object. They also wrote the file with a byte-order mark on Windows PowerShell 5.1, which Node's JSON parser rejects; the write is BOM-free now. _run_checkeddiscarded a failing setup stage's stderr/stdout, so a broken venv or pip install reported only "failed with exit code 1" with nothing actionable. It now surfaces a bounded tail of the child's own output, pip is bootstrapped as its own stage (venv --without-pip+ensurepip) so its diagnostics are no longer swallowed byvenv, and home-directory redaction covers repr-quoted and mixed-case forms child tracebacks print. Also fixed a gap the same change opened: the non-fatal "Browser setup incomplete" warning printed the raw exception instead of the redacted one, so a failing Chromium install could leak the home directory through the one message that wasn't routed through_redact(#300, Nordalux).extensions/dataforseo/install.ps1andextensions/ahrefs/install.ps1merged their MCP server entry with an embedded Python heredoc (tempfile.mkstemp+os.replace), andextensions/firecrawl/uninstall.ps1wroteConvertTo-Json -Depth 10straight to~/.claude.jsonwith no temp file. All three now follow the nativeConvertTo-Json -Depth 100+ temp-file +Move-Item -Forcepattern v2.3.0 established inextensions/firecrawl/install.ps1;ahrefs/install.ps1no longer requires Python as a result.- The
anthropic-aicrawler row v2.3.0 added toskills/seo-geo/SKILL.mdandagents/seo-geo.md, marked unverified, still does not appear on Anthropic's crawler support article (confirmed by re-fetching it: only ClaudeBot, Claude-User, and Claude-SearchBot are documented), so the row is removed rather than kept as an unverifiable guess.
- A configured HTTP proxy is validated before it is exempted from the DNS-pinned
scope. The proxy host
requestsselects for a URL is exempt from the fall-through check so the tunnel can be opened, but that host is read from the environment, soHTTPS_PROXY=http://169.254.169.254:3128turned every audit into a cloud-metadata read. The proxy now goes through the hostname blocklist andis_safe_ipon every address it resolves to, and a proxy on loopback, RFC 1918, RFC 6598, link-local, or a metadata address is refused with an error naming the address. This narrows #280: a loopback CONNECT proxy is no longer trusted. (#280, #295) CLAUDE_SEO_LOCAL_TARGETSallows auditing a local dev server, a staging host, or a machine reached over Tailscale, without the blanket "allow private" switch that would follow any private URL found on a crawled page. It is a comma-separated list ofhostorhost:portentries, consulted only for the first, top-level URL. Redirect targets, subresources, and the Playwright route handler stay fail-closed; cloud metadata endpoints are refused even when listed;is_safe_ipreads no environment. Unset, the policy is unchanged. Documented in SECURITY.md and theseo-technicalskill. (#211)- All 14 agents that fetch or render external content (via
fetch_page,render_page,parse_html, orWebFetch) now carry explicit untrusted-content guidance: treat fetched content as untrusted data, never as instructions to follow.seo-flowalready had this; the other 13 agents were missing it. Fixes #291.
content_humanize.pynow strips invisible Unicode watermark characters (zero-width codepoints, directional marks/overrides, tag characters) and normalizes exotic spaces before the AI-phrasing pass, which those codepoints otherwise defeat by breaking\bword boundaries. Emoji sequences (ZWJ, variation selectors) are preserved. Theseo-contentskill documents the new triggers and the scope limits (statistical watermarks are untouched; intended for the user's own drafts).- Templated-metadata detector (
scripts/metadata_template.py): flags meta descriptions that restate their own title tag verbatim and then close with a stock call to action, the shape bulk metadata jobs produce site-wide. Deterministic string comparison, no model,method: heuristicin its output. Exposes a site-level roll-up (templated_ratio,shared_cta_phrases,site_risk) because duplicated/templated metadata is a site-scale signal, not a per-page one. Wired intoseo-page, theseo-contentagent, the quality-gates meta description table, and theseo-programmaticuniqueness gate, which measures body copy only and therefore cannot see this. fetch_page.py --jsonexposes full response metadata and content for raw and rendered fetches, including structured fetch errors (#282).fetch_page.py --json --max-text Ntruncates content fields, matchingrender_page.py's option.
- Google Search guidance refreshed through 2026-09-10 from Google-owned sources: site reputation abuse enforcement now differs for EEA searchers (2026-08-28), region-specific aggregator and supplier units are documented for the EEA, South Africa, and Turkiye (2026-09-08), AI Mode adds travel booking and price tracking (2026-08-27), and the CrUX pass-rate figure moves to the August 2026 dataset.
safe_requests_getandsafe_requests_headsend browser-like default headers instead ofUser-Agent: python-requests/x.y.z, which managed WAFs answer with 403/406 and SSR frameworks answer with an empty client-side shell, both of which callers were analysing as if they were the real document. The values arefetch_page.py's and now live inurl_safetyas the single source of truth, withfetch_page.pyimporting them back.Accept-Languageis not sent unless the caller passes one: announcingen-USmakes a multi-locale site serve its English variant, which corrupts hreflang and international audits. (#200)
uninstall.ps1failed on Windows PowerShell 5.1 with "A positional parameter cannot be found" because it used the three-argumentJoin-Pathform that only PowerShell 7 accepts; caught by the new 5.1 smoke job.content_quality.pytokenises CJK text so Korean, Japanese, and Chinese pages get a real score instead of collapsing to one token, and reports acoverageobject (entity_density: not_computed,phrase_lists: english_only) plus a human note when the detected script means part of the composite was not computed, so a CJK score is not presented as comparable to an English one (#263).- Every live fetch failed behind a configured HTTP proxy: the pinned resolver
refused to resolve the proxy's own address, so nothing left the process. The
proxy host
requestsselects for the URL is now exempt from that check, and only that host. (#280) - The JSON-LD hook now validates every
application/ld+jsonblock regardless of attribute order, CSPnonce,idordata-*attributes, tag case, or an unquoted type value; such blocks were previously skipped without validation. - The JSON-LD hook no longer reports runtime template expressions (JSX, Vue, Svelte, template literals in component files; PHP and EJS everywhere) as invalid JSON, while a malformed literal in plain HTML is still reported.
- The JSON-LD hook accepts the schema.org
@contextwith a trailing slash, in list form, and in{"@vocab": ...}object form. google_report.pyno longer labels a plain-string finding as "Info" in the executive summary's critical-issues box or in the full-audit category findings; the "Info" prefix/badge now appears only when the finding is a dict that carries an explicitseverity.parse_html.pynow detectsrel="canonical"andrel="alternate"(hreflang)<link>tags case-insensitively, sorel="Alternate"orREL="Canonical"are no longer silently dropped. Credit to #269 for the report that prompted this investigation.- AI crawler claims are now checked against the crawler that actually governs them.
GPTBotwas documented as "ChatGPT web search" in theseo-geocrawler table; it is OpenAI's model-training crawler, whileOAI-SearchBotis what determines ChatGPT Search citability.Google-Extendedgoverns Gemini/Vertex training and grounding only and is no longer treated as a Google Search readiness signal (Google Search, AI Overviews, and AI Mode all followGooglebot). The same GPTBot-shaped conflation was found for Claude:ClaudeBot(Anthropic's training crawler) was listed as a search-visibility crawler inseo-geo's table, agent, and recommendation line, whileClaude-SearchBot(the crawler that actually governs Claude search citability) was missing entirely.seo-technicalalready hadClaudeBotcorrectly labelled training-only, soseo-geoand theseo-geoagent were brought into agreement with it rather than the other way around. AddsClaude-SearchBotandApplebot-Extended(Apple's training-opt-out token, distinct fromApplebotsearch indexing) to both skills, a claim-to-bot mapping table for all four vendors, citations to each vendor's own crawler documentation (OpenAI, Google, Anthropic, Apple), the missingOAI-SearchBotrow to theseo-technicalcrawler table, and requires training access and search citability to be reported as separate findings. Theanthropic-airow is marked unverified: it does not appear on Anthropic's current crawler support article. fetch_page.py --jsonemitted a different key set for the raw path than the rendered path (raw dumpedfetch_page()'s own dict as-is; rendered dumpedrender_page()'s dict as-is). Both paths now go throughrender_page._json_summaryafter the raw result is mapped onto the render_page contract, so--jsonoutput has one shared shape and--max-textapplies to both (#297).install.ps1crashed on Windows PowerShell 5.1 before it could even check whether Python was installed:Test-PythonCandidate's-Argsparameter was a mandatory[string[]], which 5.1 rejects when called with an empty array, andResolve-Pythoncalls it that way for thepython3/pythoncandidates (#207).- The Windows installer smoke workflow only ran the install/verify/uninstall
sequence under PowerShell Core (
pwsh); a parallel job now runs the same steps under Windows PowerShell 5.1 (shell: powershell), which is what actually caught #207. - The dataforseo, firecrawl, ahrefs, and banana extension installers wrote
their MCP server block to
~/.claude/settings.json, a key Claude Code does not read from that file, so the server never loaded and reinstalling could not fix it. They now write~/.claude.json(the fileclaude mcp addwrites) across install/uninstall scripts, banana's Python helpers, and the setup docs.bing-webmaster,profound, andserankingwere left alone: they write theenvkey, which settings.json does support (#204). extensions/firecrawl/install.ps1andextensions/banana/scripts/ setup_mcp.pynow write~/.claude.jsonatomically (temp file in the same directory, thenMove-Item -Force/os.replace), and the PowerShell serialisation depth is raised from 10 to 100 so an existing~/.claude.jsonwith deeply nested config round-trips intact instead of being flattened.scripts/backlinks_auth.py's token file hardening was a no-op on Windows: it had no write path at all, and its permission story on POSIX (none) did not matchgoogle_auth.py's OAuth token handling. It now sharesgoogle_auth._chmod_quiet, gains asave_config()that mirrorsgoogle_auth.py'sos.open/os.fchmod0o600 write pattern, and bothsave_config()andload_config()make a best-efforticaclscall on Windows to restrict~/.config/claude-seo/backlinks-api.jsonto the current user, since POSIX mode bits do not restrict NTFS ACLs (#290).- 17 scripts raise
sys.exit(1)at import time when an optional dependency (requests,bs4,playwright,googleapiclient,google.analytics) is missing. Any test module that imports one of these at module scope aborted the whole pytest session on a minimal install instead of skipping. 8 of the 17 (bing_webmaster,commoncrawl_graph,crux_history,fetch_page,moz_api,nlp_analyze,pagespeed_check,parse_html) had test modules that needed the guard (gsc_query's 3 test modules already had it; the other 8 of the 17 have no test module that imports them at module scope, so nothing to guard).url_safety.pyitself hard-requiresrequests(by design), so every script that imports it transitively hits the same failure; guarded those test modules too (domain_history,gbp_deprecation_lint,parasite_risk,agent_ux_check/render_page,indexnow_submit,url_safety's own suite, and the newbacklinks_authhardening tests). 17 test modules gained apytest.importorskipguard in total. Verified in a throwaway venv with onlypytestandbeautifulsoup4installed: the suite completes (206 passed, 24 skipped, no errors). unlighthouse_run.pyno longer passes--max-routesas--scanner '{"maxRoutes": N}', a CLI flag unlighthouse-ci's parser never reads (the crawl silently ran uncapped). Route count and a new per-page timeout are now set via a generatedunlighthouse.config.mjspassed with--config-file, confirmed against unlighthouse's CLI source and docs.ci-result.jsonis parsed as the array the defaultjsonSimplereporter actually writes, with a tolerant fallback for thejsonExpandedobject shape, instead of assuming a dict.extensions/unlighthouse/install.shno longer aborts on a marketplace/plugin install: it now also checks${CLAUDE_PLUGIN_ROOT}and the plugin cache before requiring the manual~/.claude/skills/seolayout. Fixes #189.- Raised
maxTurnson all 16 agentsseo-auditcan spawn (seo-technical20→45,seo-content15→45, and thirteen others that were below 30) so a large-site audit doesn't hit its turn budget before finishing. Every one of those agents now writes a partial findings file after its first analysis pass and overwrites it with the complete findings at the end, so a turn-budget stop never throws away completed work;seo-audit's error-handling table documents the same contract for the orchestrator. Fixes #177, #272.
commoncrawl_graph.pyno longer writes outside its cache directory: the--releasevalue was interpolated raw into the cache filename, so--release ../../../../tmp/xescaped the cache directory and_save_cachewrote there. Malformed releases are now rejected at the CLI and path containment is asserted.domain_history.pyno longer follows an unvalidated WHOIS referral. The IANArefer:host is resolved and validated throughurl_safetyand dialled at the pinned address; an unusable referral degrades to IANA's own answer.url_safety.is_safe_ipnow refuses the RFC 6598 shared address space (100.64.0.0/10), where Alibaba Cloud serves instance metadata, and judges IPv4-mapped IPv6 literals by their embedded address. This also refuses Tailscale addresses; see SECURITY.md.- WeasyPrint floor raised to 70.0 (PYSEC-2026-3940) and requests to 2.34.2
(CVE-2026-25645).
pip-auditpasses. - SECURITY.md describes only reporting channels that exist: private vulnerability reporting is enabled and the unreachable email fallback is gone.
- The plugin installs from the claude.ai-hosted marketplace. Hosted sync rejects any
plugin with a top-level
bin/directory, so the launcher moved toscripts/claude-seoand every skill, agent, and doc calls it as"${CLAUDE_PLUGIN_ROOT}/scripts/claude-seo" run <script.py>. Manual installs copy the launcher to~/.claude/skills/seo/scripts/claude-seoand rewrite that token to the absolute path. A layout test keepsbin/from coming back. Fixes #298 and #199. - CI runs the full suite on Windows and macOS, audits
requirements.txtwithpip-audit, and no longer swallows a failed dependency install in the v2 audit. CLAUDE_SEO_CONFIG_DIRoverrides the config and ledger location, so the ledger tests run against an isolated file.BANANA_HOMEdoes the same for the Banana ledger.preload_check.py --fail-under Nturns the score into an opt-in gate.- Regression coverage for the backlink report validator, the schema-hook UTF-8 output,
CRLF checkouts, PSI null category scores, ledger concurrency, the hosted plugin layout,
and every ledger kind accepted by
seo_updates.py.
- Dependency floors raised by Dependabot: google-auth 2.56.2, courlan 1.4.0, playwright 1.62.0, numpy 2.2.6 (a major bump from 1.26; matplotlib moves to 3.9.0, the first line that supports numpy 2).
seo-technicaltreats dynamic rendering as a workaround to flag, not a target state, and recommends SSR, SSG, or CSR with a preferred-framework list.- The
seo-backlinksskill andfree-backlink-sources.mdno longer contradict each other on Common-Crawl-only reports: no numeric score is produced, andvalidate_backlink_report.pyfails a report that carries one. - The Repository Topology section of CLAUDE.md describes the two single-remote checkouts and the cherry-pick promotion flow actually in use.
- The three
test_sync_flow.pytests that call the live GitHub API run only whenCLAUDE_SEO_NETWORK_TESTS=1; both CI test jobs set it withGH_TOKEN.
- The DataForSEO and Banana cost ledgers lost concurrent writes and could reset spend
history after a truncated write. One exclusive lock now spans each read-modify-write,
writes are atomic, Windows falls back to
msvcrt, and a corrupt ledger fails closed. pagespeed_check.pyraisedTypeErrorwhen a Lighthouse category returned a null score; the category is now skipped and the page is kept.preload_check.pyexited 1 on every successful run that scored below 75. A completed analysis exits 0. (#281)consistency_check.pyreported every FLOW-locked prompt as a hash mismatch on CRLF checkouts; it folds CRLF before hashing.seo_updates.py --kind documentationwas rejected by argparse while the ledger used that kind; the CLI and the schema now share one list.- Optional Google, Bing, and rendering dependencies missing at import time no longer
abort the whole pytest session (
pytest.importorskipin the affected modules). - The schema-hook tests decode the hook's UTF-8 output explicitly instead of through the locale codec, which is cp1252 on Windows.
- Focused regression coverage for manual-install data packaging, runtime imports, JSON-LD graph validation, empty rendered pages, accessibility-tree capture, JSON output bounds, and managed-runtime command references.
- Strict Google update-ledger validation for chronological order, known event kinds, exact approved hostnames, unique names, and verification dates.
- Refreshed Google Search guidance through 2026-08-25 using Google-owned sources, including the August spam update, Preferred Sources, Search Console platform properties, review transparency, merchant schema, canonical timing, and AMP.
- Updated Lighthouse guidance to 13.4.1, including PSI API support for Agentic Browsing and the Node.js 22.19 minimum for the CLI.
- JSON render output now preserves full content by default and supports explicit,
measured text bounds through
--max-text.
- Manual installs now include the update ledger, and runtime setup validates the
split
lxml_html_cleandependency required by current lxml releases. - JSON-LD hooks now traverse top-level graphs, reject malformed graph members, handle non-UTF-8 input safely, and avoid ordinary-language placeholder false positives.
- Agent-UX reports now fail closed on empty rendered documents, capture Chromium's accessibility tree through CDP, and expose partial or failed capture states.
- User-facing guidance now routes bundled Python modules through
claude-seo run.
Community maintenance release following a full review of every open issue and pull request. Accepted findings were reconciled against the current release base, with superseded or unsafe patches excluded. No breaking command changes. Full suite: 410 passing tests.
- Managed cross-platform Python runtime with
claude-seo run,/seo setup, and/seo doctor. Plugin environments persist under Claude's plugin data directory, manual installs use an isolated local environment, and bundled scripts no longer depend on the caller's working directory or a hardcoded Python executable. - SSRF-safe sitemap discovery covering robots.txt directives, common CMS locations, stale fallbacks, bounded responses, and strict cross-host validation.
- Contributor and issue-reporter acknowledgements for the complete maintenance review cycle.
- GSC queries now honor exact result limits, validate dimensions before API access, support dimensionless totals, and report whether aggregate totals are complete.
- Bing Webmaster backlink commands now use supported endpoints, bounded pagination, deduplication, registered-property comparison, partial-failure reporting, and sanitized errors.
- DataForSEO agents now expose only the required MCP tool family and fail closed when it is absent.
- Banana extension paths now work in plugin and manual layouts.
- OAuth token persistence is safe on Windows, hook and child-process output is UTF-8, and report input handling is explicitly UTF-8.
- SPA rendering recognizes common hosted builders, uses bounded DOM stabilization, and extracts JSON-LD before HTML truncation without leaking malformed fragments in routine output.
- Removed unsupported FAQPage rich-result benefit claims and templates after Google's retirement of FAQ rich results.
- GitHub Actions checkout and Python setup actions moved to v7. Anonymous FLOW synchronization now retries rate-limited requests through the authenticated path when available.
- Runtime dispatch is allowlisted, setup upgrades are rollback-safe, diagnostics redact paths and identity-like values, and structured-data output is bounded.
- Removed the maintainer email address from public package and citation metadata.
Prompt-hygiene alignment to the Fable 5 prompt principles. No behavior, routing, or output changes.
- Removed CAPS emphasis words (MUST, NEVER, ALWAYS, and a CRITICAL RULES heading) used as emphasis across skills and agents; severity status tokens left intact.
- Swept em-dashes and en-dashes from prose to commas, colons, and hyphens across the prompt surface (code fences untouched).
- Trimmed five over-long skill descriptions.
Full-review maintenance pass (2026-07-09/10). 319 adversarially verified findings from a multi-engine review (7 Google research streams, 20 currency audit slices, 6 refuter slices); all fix bundles applied. No breaking changes.
- GBP Q&A false-deprecation removed from
scripts/gbp_deprecation_lint.pyandskills/seo/references/local-seo-signals.md(Google: Q&A is category/region-limited, not retired). - AI Mode model claim corrected to Google's confirmed "custom version of Gemini 2.5"; AI Overviews / AI Mode user counts attributed to third-party I/O 2026 reporting.
- Stale Gemini image-model IDs refreshed across seo-image-gen and the banana extension;
dead
scripts/presets.py/cost_tracker.pyinvocations repointed. - rel=next/prev pagination and Publisher Center news-sitemap guidance corrected; WebMCP audit IDs aligned with Lighthouse 13.2.0 release notes; GA4 AI Assistants source list fixed.
- CLAUDE.md tree/table brought to v2.2.1; PRIVACY.md now discloses the Bing Webmaster / IndexNow extension; README offline phrasing corrected; TROUBLESHOOTING de-v1'd.
hooks/validate-schema.pynow supports the documented stdin hook-event contract.
scripts/consistency_check.py: path-aware reference-graph gate (dead refs, routing tables, FLOW lock, orphans) with CI coverage viatests/test_consistency_check.py.
Google-currency refresh. Reconfirms the suite against the latest Google updates, verified against Google-primary sources via a multi-agent reconfirmation run. Documentation/data accuracy only — no code behavior change beyond two added IPTC vocabulary values. No breaking changes.
- Lighthouse:
13.0 (Oct 2025)→13.4.0 (June 2026, latest stable); corrected the 13.0 description (insight-based audits, not "reorganized scoring weights"); documented the new Agentic Browsing category (created 13.2.0, default 13.3.0, disabled in the PSI REST API in 13.4.0) as a fractional pass-ratio, not a 0–100 score. (agents/seo-performance.md,skills/seo/references/cwv-thresholds.md,skills/seo-technical/**) - WebMCP: replaced the "early preview / not before 2027" framing with the live Chrome 149 origin trial (sign-up open 2026-06-09) and the three shipped Lighthouse audits (
registered-webmcp-tools,forms-missing-declarative-webmcp,webmcp-schema-validity). - GEO: AI Overviews reach
1.5B→ 2.5B+ MAU (I/O 2026); added that Google Search ignores llms.txt (docs, 2026-06-15); reconciled the unified AI-Search experience with the two-citation-engines model. - E-commerce: UCP
"version": "1.0"→ date-based (current2026-04-08) with canonical URLs; AP2 donated to FIDO (2026-04-28); Universal Cart / Native-vs-Embedded checkout / Lodging+Food verticals. - Content/E-E-A-T: removed the fabricated "December 2025 watershed / 71-67-52% drops" claim; set the plugin's internal E-E-A-T scorecard weights to 20/25/25/30 (Trust highest) — an internal scoring model; Google publishes no numeric E-E-A-T weights, only that 'trust is most important'.
- Schema: Dataset is not discontinued (Dataset Search still consumes it); corrected the deprecated-types tooling-removal timeline.
- Technical: softened mobile-first absolutism + added content-parity; added the back-button hijacking spam policy; page-experience framing; Googlebot 2MB/64MB fetch limits; crawling-docs migration to developers.google.com/crawling; user-triggered fetchers (Google-Agent etc.) ignore robots.txt.
- Verticals: JPEG XL shipped (Chrome 145, flag-gated); two new IPTC DigitalSourceType values; hreflang ISO-15924 script subtags + signal hierarchy; sitemap 50MB/extension-subtype rules; Apple Business rename; Ask Maps / AI local pack.
- Ledger: refreshed
data/google-updates.json(Feb 2026 Discover, March 2026 spam, May 2026 core completion, back-button policy, GSC gen-AI report, etc.); fixed the INP date and replaced 301'dranking-update-historyURLs with the Search Status Dashboard.
seo-google: Search Console Generative AI performance report, GSC impressions logging-error caveat (2025-05-13 → 2026-04-27), GA4 AI Assistants channel.scripts/iptc_ai_label.py:algorithmicMediaandcompositeWithTrainedAlgorithmicMediaDigitalSourceType values.- Documentation: primary-source reconfirmation details folded into public references.
Full audit of every /seo command + subcommand (25 skills + 8 extensions), via deterministic wiring checks + 5 Codex sub-agents; findings were folded into this release.
- Wiring bugs:
seo-sxoagent calledparse_html.py "<url>"(script needs--url);seo-sxoandseo-localreferenced DataForSEO tools not in the catalog (google_organic_serp/keyword_data/local_business_data/google_local_pack_serp/business_listings→serp_organic_live_advanced/kw_data_google_ads_search_volume/business_data_business_listings_search);content-briefusedserp_google_organic_live_advanced→serp_organic_live_advanced; Bingsubmit-batch/verify-indexnowwere missing the required--hostflag. - Broken docs:
docs/COMMANDS.mdgoogle examples used non-existent command names (psi/gsc-queries/indexing-notify/ga4-organic/check) → realpagespeed/gsc/inspect/index/ga4; maps usedgeogrid/audit→grid/gbp; "7 specialist subagents" → "up to 15". - Shared-reference paths:
seo-schema/seo-backlinkspointed at`references/...`as if local → repointed to the sharedseo/references/location. - COMMANDS.md completeness: documented every previously-unlisted subcommand (backlinks, ecommerce, dataforseo cost/serp-images, flow, cluster, sxo, hreflang, images) and added the missing extension command tables (ahrefs, bing, profound, seranking, unlighthouse). Coverage now 100% (every skill subcommand is indexed).
Reconciled the public docs/manifests with the v2.1.0–v2.2.1 code state via a parallel per-file pass with an independent verifier gate. No behavior change.
- README.md: added a "Since v2.0.0" recap (v2.1.0–v2.2.1); reframed the command surface to 30 user-invocable
/seocommands and added the five missing extension commands (ahrefs,seranking,profound,bing,unlighthouse) to the table; corrected stale figures: DataForSEO22 → 23commands, manifest14 → 15assertions, audit fan-out "6 parallel" → "up to 15 core audit agents; optional extension agents such as seo-dataforseo may run in addition", and the v2 test narrative to39 → 326(url_safety suite = 91 cases); reworded the "zero-network" overclaim (audits still fetch target URLs) and the Playwright optional/auto-install wording. - docs/COMMANDS.md: added the five extension commands to the Quick Reference table (detail sections already existed).
- docs/ARCHITECTURE.md: extension tree and capability table now cover all 8 extensions (added Ahrefs, SE Ranking, Profound, Bing Webmaster, Unlighthouse).
- PRIVACY.md: data-transfer disclosure for the five newer extensions (Unlighthouse marked local-only — no third-party egress); same zero-network reword.
- SECURITY.md: reconciled the internal
tests/test_url_safety.pycount contradiction (52+vs122) to the verified 91 cases (31 functions) in both places. - CONTRIBUTING.md: corrected the SSRF guidance — fetchers route through
scripts/url_safety.py(validate_url()/safe_requests_session()), notgoogle_auth.py(OAuth-only). - AGENTS.md: removed the non-existent "ASO" extension (listed the real 8); added
seo-content-briefandseo-flowto the tree and command table. - Other docs:
MIGRATION-v1-to-v2.md("Three" → six rich-result types + v2.1–2.2.1 forward pointer),MCP-INTEGRATION.md(DataForSEO22 → 23),TROUBLESHOOTING.md(schema false-positive note now covers the 2025 retirements + the 2026-05-07 FAQ retirement),INSTALLATION.md(Playwright wording),WORKFLOW-public-private.md(state section refreshed to v2.2.1 / 2026-06-22). .claude-plugin/marketplace.json: descriptions refreshed for the 2.1–2.2.1 currency + command audit, and both trimmed under the 500-char registry cap (the plugin-entry description was previously over cap at 573).extensions/dataforseo/:README.md,install.sh, andinstall.ps1corrected22 → 23commands.
Corrections from a live re-confirmation of every time-sensitive Google claim against Google-primary sources fetched 2026-06-22. Confirmed claims were updated and uncertain claims relabeled. All are precision/labeling fixes — the v2.2.1 currency substance was confirmed accurate.
- E-E-A-T weights are an internal model, not Google's. Google publishes no numeric E-E-A-T weights (only "trust is most important"). Relabeled the 20/25/25/30 split as the plugin's own scoring model in
agents/seo-content.md,skills/seo-content/SKILL.md, README, and this changelog. - FID was never in Lighthouse. Removed "Lighthouse" from the FID-removal tool list across 7 files (it is a lab tool and never reported the field-only FID metric); the removal applies to Chrome's field-data tools (CrUX API, PageSpeed Insights).
- QAPage is not the "FAQ replacement." Reworded README + the v2.1.0 changelog note: QAPage is the type for genuine Q&A pages, not a replacement for FAQ rich results (per the primary
qapagedoc). - June 2025 deprecation date corrected
2025-06-19 → 2025-06-12(announcement date) indata/google-updates.jsonand the deprecated-types reference. - January-2026 tooling sunset scoped to Practice Problems (the only type Google documents it for) in the deprecated-types reference, instead of generalizing it to all retired types.
- Dead citation repaired: the HowTo retirement source
…/2023/09/structured-data-changes(HTTP 404) repointed to the real post…/2023/08/howto-faq-changes.
Security, cross-platform, and data-accuracy release. Folds the v2.1.0 currency content into the first public ship and closes the full open-issue and PR backlog. No breaking changes.
- Installer credential injection (blocker). The DataForSEO, Firecrawl, and Banana installers interpolated user-supplied credentials into a
python3 -csource string, allowing arbitrary code execution at install time when a credential contained'''. Credentials now pass asargvthrough a quoted heredoc, and the settings file is written atomically with0600permissions (shell installers plus the DataForSEO PowerShell installer). Found by an independent audit. - SSRF parser-differential bypass.
url_safety.validate_urlaccepted authority-confusion URLs such ashttps://127.0.0.1:6666\@1.1.1.1, whichrequestsconnects to the internal host. The validator now rejects backslash and userinfo authority confusion, covering every caller. Reported by @Fushuling (#110). - Google API key leak.
pagespeed_check,crux_history,nlp_analyze, andlcp_subpartsput the API key in the request URL and echoed it on error. Keys now travel in theX-Goog-Api-Keyheader with redacted error output. Reported by @webgunnz (#122); header approach from #104 (@fayerman-source). - Post-audit hardening pass. Extended the credential-injection fix to the extension uninstallers and the Banana config probe (argv through quoted heredocs). Added Bing Webmaster API key redaction on transport errors. Switched the DataForSEO and Firecrawl PowerShell installers from
PtrToStringAutotoPtrToStringBSTRso SecureString credentials decode correctly under PowerShell on Linux and macOS. - Secret-scan CI gate. A new job in
ci.ymlandv2.ymlfails the build when any tracked file contains a high-signal credential pattern (Google, GitHub, AWS, Google OAuth, OpenAI, Slack); test fixtures and documented placeholders are allowlisted..gitignoreextended to cover more credential and key formats. Verified against the full history and tracked tree: no real secret present.
- GSC false "0 clicks" totals (#130). Site totals were summed from per-query rows, which GSC anonymizes for low-volume queries. Totals now come from a dimensionless aggregate query. Reported by @fayerman-source.
- Windows drift_baseline portability (#114, #124). Removed
/dev/stdout, use a tempfile fetch-to-parse handoff witherrors="replace", and handle the Microsoft Store Python alias (PRs #117, #128, #111, #115, #125). - Cross-platform PostToolUse hook (#102, #112, #120). The JSON-LD validator runs through a Node launcher that resolves
python3/py, fixing failures on macOS and Linux without a barepython(PR #101). - fetch_page UTF-8 double-encode (#121). Honor the Content-Type and
<meta>charset deterministically when the server omits a charset. - GSC deprecated
indexedfield (#113). No longer surfaced (it always returned 0). - NLP entity metadata (#103). Use the V1
analyzeEntitiesendpoint so Knowledge Graphmid/wikipedia_urland salience are returned. - Moz free-tier auth (#100). Use the Links API REST endpoint with HTTP Basic auth.
- FAQ schema hook. FAQPage is no longer flagged (FAQ rich results were retired in May 2026, but the markup still aids AI Mode); deprecated and retired types still block.
- Broken sub-skill reference paths.
/seo localand/seo mapsinstructed the model to loadreferences/*.mdfrom directories that do not exist; both now point to the sharedskills/seo/references/files they always intended. - seo-cluster template now ships.
templates/cluster-map.htmlwas excluded by an over-broad.gitignorerule, so installed users never received the interactive cluster visualization. It is now tracked. - Unlighthouse and Ahrefs extension invocations. The Unlighthouse extension called a non-existent npm package (
unlighthouse-cli); it now usesunlighthouse@0.13.5. The Ahrefs extension is pinned to@ahrefs/mcp@0.0.11and invokes the package's realmcpbinary. - FLOW prompt dead links. 82 links across the 41 FLOW prompt files pointed at the upstream folder layout that does not exist in this plugin.
sync_flow.pynow rewrites them to the flattened layout on every sync, and the existing files were repaired. - sync_flow offline crash.
--dry-runraised an uncaught network error when GitHub was unreachable; it now exits cleanly with an actionable message. - Install slug and stale figures. Corrected the marketplace slug in the install docs to
claude-seo@agricidaniel-claude-seo, theCITATION.cffrelease date, the README test count (326), the AGENTS.md script count (50), and the README FAQ guidance.
- Full-audit report persistence and audit-aware report builders (#51, #61).
- ruff configuration (#123) and
pyproject.tomlauthors and keywords (#118). - Regression tests for installer injection, GSC totals, and the schema hook policy.
- Plugin description trimmed under the 500-character registry cap (#99).
- Docs normalized from bare
pythontopython3; CLAUDE.md and AGENTS.md script inventory corrected to 50; README test count updated. - Corrected the inert
user-invokablefrontmatter key touser-invocable. - Pinned the Ahrefs, Unlighthouse, and DataForSEO npm packages to exact versions across installers and prewarm steps.
- Documented the
/seo content-briefcommand in the command reference and README, added/seo flowto the project command table, and reconciled the command count to the 25 the orchestrator routes.
- Removed the duplicate root
CODEOWNERS; CI compiles everyscripts/*.pydynamically; marketplace extension count corrected from 7 to 8; dependency floor bumps (Dependabot #105 to #109, #116). - Removed the orphaned
branding/preview tooling (it referenced deleted diagram assets) and the inactive npm Dependabot watcher (the repo ships no npm manifest). Hardened the Python hook probe against environment-specificEPERM, gave Bananavalidate_setup.py --helpproper argument handling, and repaired stale internal documentation links. Full suite at 326 passing.
Knowledge-currency refresh for Google's May 2026 wave: the May 2026 core update, Google I/O 2026 (Gemini 3.5 Flash now powers AI Mode globally; AI Mode past 1B monthly users), and the May 7 2026 retirement of FAQ rich results. No architecture, API, or command changes — every v2.0.0 entry point still works.
data/google-updates.json: four primary-source-verified entries — March 2026 Core Update (promoted fromunverified[]after Google status-dashboard confirmation; Mar 27 to Apr 8 rollout), FAQ rich result retirement (May 7), Google I/O 2026 / Gemini 3.5 Flash in AI Mode (May 19), and the May 2026 Core Update (May 21).unverified[]is now empty;last_verifiedbumped to 2026-05-25.skills/seo-geo/SKILL.md: AI Mode is now modeled as a distinct citation engine from AI Overviews (Ahrefs: only 13.7% URL overlap across 540K query pairs), with its own row in the platform table, the Gemini 3.5 Flash + 1B-user stats, content recency as a citation lever (~3x for content under 3 months, SE Ranking), and the "~44% of AI citations come from the first 30% of the page" finding.skills/seo/references/schema-types.md:QAPageadded as the active type for genuine user Q&A (the active type for real Q&A pages — not a replacement for FAQ rich results).tests/test_schema_v2.py:test_faq_rich_results_retirement_documentedlocks the May 7 2026 FAQ retirement + QAPage-for-Q&A guidance across the canonical schema references.
- FAQ schema guidance corrected across the canonical sources (
schema-types.md,deprecated-types-2024-2026.md,seo-schema/SKILL.md,agents/seo-schema.md,seo/SKILL.md,seo-page/SKILL.md,seo-content/SKILL.md,seo-plan/assets/saas.md): FAQ rich results are fully retired for all sites as of May 7, 2026, superseding the Aug 2023 gov/health framing. FAQPage stays Info-priority as an AI/entity signal (never a Critical removal);QAPageis the type for genuine Q&A pages. skills/seo-content/SKILL.md: AI Mode description updated to the Gemini 3.5 Flash / 1B-user / two-citation-engine reality.- Version bumped to
2.1.0acrossplugin.json,pyproject.toml,CITATION.cff,install.sh,install.ps1, and 32 SKILL.md files (seo-content-briefstays at 1.0.0 per COMMUNITY_OVERRIDES). Gated bytests/test_manifest_consistency.py.
v2 is backward-compatible by design — every v1.x command, script signature, and skill entry point still works. The release lands a hardened SSRF + DNS-rebinding safety layer, shared headless rendering across every fetcher, QRG-aligned content gates, four new Schema.org generators, five new MCP extensions, and multi-platform portability. Full narrative in docs/MIGRATION-v1-to-v2.md.
- 248 tests (6.4× the v1.9.9 baseline of 39), every known SSRF bypass class closed at parse time.
- 5 new MCP extensions: Ahrefs, SE Ranking, Profound, Bing Webmaster, Unlighthouse.
- 4 new Schema.org generators: Reservation, OrderAction (potentialAction), DiscussionForumPosting, ProfilePage.
- Multi-platform portability for Codex CLI, Cline, Aider (alongside existing Cursor + Antigravity).
- Foundation:
scripts/url_safety.py(canonical SSRF + DNS-rebinding module, 83 test cases),scripts/render_page.py(shared Playwright Chromium renderer with--mode autoSPA detection, trafilatura extraction, htmldate publication-date extraction, 27 test cases). 8 fetcher subagents (seo-technical, seo-content, seo-schema, seo-geo, seo-local, seo-ecommerce, seo-backlinks, seo-sxo) now route throughrender_page. - Content quality:
content_quality.py(QRG filler / AI-pattern / information-density scorer),content_humanize.py(40+ deterministic AI-phrasing replacements),content_verify.py(claim extraction + citation-gap detection),domain_history.py(WHOIS-driven expired-domain abuse check),seo_updates.py+data/google-updates.json(18 primary-source-verified Google updates, 1 documented-unverified gap-analysis claim). - Technical depth:
preload_check.py(Speculation Rules + bfcache + prerender + LCP preload audit),indexnow_submit.py(Bing/Yandex/Seznam/Naver IndexNow submitter),lcp_subparts.py(LCP decomposition via CrUX),unlighthouse_run.py(multi-page Lighthouse via Unlighthouse CLI). - Schema completeness:
schema_generate.py(reservation,order,discussion,profilesubcommands),schema_ecommerce_validate.py(Product schema policy validator:hasMerchantReturnPolicy,shippingDetails,MemberProgram, EUenergyEfficiencyClass,ProductGroup), reference docskills/seo-schema/references/deprecated-types-2024-2026.md. - AI search:
parasite_risk.py(site-reputation-abuse risk scanner per Nov 2024 Google policy),skills/seo-geo/references/llmstxt-evidence.md(evidence-based reframe of llms.txt as dev-tooling, not citation lever). - Local + international + privacy:
gbp_deprecation_lint.py(retired GBP chat /.business.site/ Q&A detector),skills/seo-google/references/dma-consent-mode-v2.md(EU CTR diagnostic + softened cookieless framing),skills/seo-hreflang/references/machine-translation-qa.md(untranslated-MT detection per Jan 2025 QRG §4.6.5). - Portability:
portability_check.py(cross-platform SKILL.md frontmatter lint), AGENTS.md tool-name compatibility table for Codex CLI, Cline, Aider. - Release signing:
release_sign.py(SHA-256 manifest of every git-tracked file),verify_release.py(verify a checkout against a signed manifest). - Governance:
.github/CODEOWNERS,.github/dependabot.ymlextended with npm ecosystem,.github/workflows/v2.yml(workflow_dispatch only),SECURITY.mduplift (threat model, 90-day coordinated disclosure timeline, residual risks).
scripts/google_auth.py:validate_urlnow delegates tourl_safety.validate_url. Strict variant available asurl_safety.validate_url_strict.scripts/fetch_page.pyexposes--render {auto,always,never}. Defaultautoruns raw fetch then renders if SPA signals detected;alwaysforces Playwright;neverpreserves v1 behaviour.scripts/capture_screenshot.pyusesurl_safety.make_safe_playwright_route_handleras defense-in-depth against subresource SSRF (data: allowed, private resolutions aborted, AF_UNSPEC IPv6-aware).- OAuth token files are now written with
os.open(path, O_WRONLY|O_CREAT|O_TRUNC, 0o600)+ explicitos.fchmod(fd, 0o600). Legacy0o644files are remediated in place on next_load_oauth_token. plugin.json/marketplace.json/pyproject.toml/CITATION.cff/install.sh/install.ps1/ 32 SKILL.md files: version bumped to2.0.0. The 13-assertion manifest test (tests/test_manifest_consistency.py) gates this.
- HIGH — DNS rebinding via redirect target.
_pin_dnspreviously intercepted only the originally-pinned hostname; redirect targets fell through to the unpatched resolver. Patchedsocket.getaddrinfonow validates every resolution while pinned. Closed ina601268. - HIGH — Obfuscated IPv4 bypass in
validate_url. Decimal (2130706433), hex (0x7f000001), octal (017700000001), leading-zero (127.0.0.001,0177.0.0.1), and mixed-radix (0x7f.0.0.1) forms all returned safe. Newnormalize_hostname()canonicalizes viasocket.inet_aton. Closed in3c595c2. - HIGH — FQDN trailing-dot bypass.
metadata.google.internal.(single trailing dot) bypassed the exact-string blocklist.normalize_hostnamenow strips a single trailing dot. Closed in3c595c2. - MEDIUM — IPv6 blind spot in Playwright route handler. Resolver queried only
AF_INET. Now usesAF_UNSPECto catch dual-stack subresources whose AAAA record points at a private range. Closed in3c595c2. - LOW — OAuth file-permission TOCTOU.
os.open's mode argument is ignored if the file pre-existed. Explicitos.fchmod(fd, 0o600)on the open fd closes the race. Closed in3c595c2.
Two intentional behavioural breaks; full mitigation guidance in docs/MIGRATION-v1-to-v2.md.
scripts/backlinks_auth.pyno longer ships a silent SSRF-disabled fallback. Ifurl_safetycannot be imported, the module raisesRuntimeErrorat import time.seo-schemaflags six retired rich-result types as Critical findings (Vehicle,ClaimReview,EstimatedSalary,LearningVideo,SpecialAnnouncement,CourseInfocarousel). Replacements documented inskills/seo-schema/references/deprecated-types-2024-2026.md.
| Suite | v1.9.9 | v2.0.0 |
|---|---|---|
| Pre-existing (manifest + lazy + sync FLOW) | 39 | 39 |
url_safety (new) |
— | 83 |
render_page (new) |
— | 27 |
| Content quality (new) | — | 25 |
| Technical depth (new) | — | 17 |
| Schema v2 (new) | — | 17 |
| Parasite risk + extensions (new) | — | 22 |
| GBP lint + polish (new) | — | 8 |
| Portability (new) | — | 10 |
| Total | 39 | 248 |
Final 1.x patch release. v2 is in design; this release leaves the v1.x branch in a clean, well-documented, dependency-current state.
Independently verified across 5 rounds of GPT-5.5 xhigh code review via the Codex CLI before each PR push. Issue #92 + issue #41 closed.
- Five top-level versions, 24 in-tree skills, 3 extension SKILL.md files,
and both install scripts triangulate to
1.9.9atomically. CI guard extended from 9 to 13 assertions covering the orchestrator SKILL.md, per-skillmetadata.version, marketplace.json metadata.description + author parity, and Sub-Skills/Subagents list consistency with disk. - Five Dependabot dependency floor bumps merged as one batched PR after isolated-venv smoke-testing of the full API surface we actually use.
- Image audit now correctly detects JS lazy-loaders (Perfmatters, EWWW, generic) rather than reporting "not lazy-loaded" on heavily-optimized WordPress sites.
- Orchestrator drift in
skills/seo/SKILL.md(issue #92): line 9metadata.version: "1.9.6"was stale; descriptive headline at lines 19-21 still claimed "21 specialized sub-skills"; Sub-Skills numbered list at 176-199 includedseo-firecrawl(which is an extension, not inskills/) and was missingseo-content-brief(the PR #56 contribution). Subagents bullet list had the same drift pattern (includedseo-firecrawl, no agent file on disk; missingseo-flow, file exists). Reconciled. Numbered list now reaches 24 (the orchestrator itself is the 25th inskills/but does not orchestrate itself),seo-firecrawlmoved to a new "Optional Extensions" subsection, Subagents list now matchesagents/seo-*.mdset exactly. marketplace.jsondrift (issue #92):metadata.descriptionwas missing the "sub-agents" count claim thatplugins[0].descriptioncarried; plugin entry had noauthorobject despite v1.9.8 release notes claiming one was added in commit8514999(verification showed it was not). Both fixed. v1.9.8 entry in this CHANGELOG corrected to reflect what actually shipped.AGENTS.md:109said "17 subagents"; disk has 18. Fixed.install.shandinstall.ps1default tag pinned tov1.9.0across 4 missed release bumps (v1.9.5/.6/.7/.8). Anyone runningcurl -fsSL .../install.sh | bashgot the April 14 release, missing FLOW integration, the security audit pass, doc reconciliation, the manifest CI guard plus v1.9.8 Phase B bug fixes (Windows hook, OAuth refresh, missing imports, None guards). Bumped tov1.9.9atomically with this release.pyproject.tomlhad drifted to1.9.6while plugin.json + CITATION shipped at 1.9.8. Bumped to 1.9.9 with the release.- 23 in-tree skill
metadata.versionfields were stuck at1.9.6; 3 extension SKILL.md files were at1.9.0/1.7.2. All bumped to1.9.9.seo-content-briefdeliberately stays at1.0.0(community contribution, CI allowlist). - Image audit (issue #41):
scripts/parse_html.pynow classifies each image's lazy-loading mechanism in alazy_methodfield with five values:native | perfmatters | ewww | js-generic | none. Sites running Perfmatters, EWWW Image Optimizer, lazysizes, vanilla-lazyload, or jQuery lazy-loaders are no longer mis-reported as "not lazy-loaded".skills/seo-page/SKILL.mdandskills/seo-images/SKILL.mdare updated to consume the new field.
- CI guard extension (9 -> 13 assertions) in
tests/test_manifest_consistency.py:test_orchestrator_sub_skills_list_matches_disk: Sub-Skills list must equalset(skills/*) - {seo}; no duplicates. Regex scoped to the## Sub-Skillssection via a new_extract_section()helper.test_orchestrator_subagents_list_matches_disk: Subagents bullet list must equalset(agents/seo-*.md); no duplicates. Bullet-anchored regex.test_skill_metadata_versions_match_plugin_json: everyskills/*/SKILL.mdandextensions/*/skills/*/SKILL.mdmetadata.versionmust equalplugin.jsonversion, withCOMMUNITY_OVERRIDESallowlist{"seo-content-brief": "1.0.0"}. Scoped to YAML frontmatter only via a new_extract_frontmatter()helper, so a fenced code example showingversion: "..."cannot satisfy the check.test_marketplace_metadata_and_author_parity: marketplace.jsonmetadata.descriptionincludes both counts and they match plugin.json; plugin entryauthorparities plugin.json author forname,email, ANDurl.
tests/test_lazy_detection.py(new): 11 unit tests covering all_detect_lazy_method()branches plus an integration check onparse_html().- CI workflow (
.github/workflows/ci.yml): test job now installsbeautifulsoup4alongsidepytest, required by the new lazy-detection test that exercises real BeautifulSoup parsing.
-
5 Python dependency floor bumps (batched as a single PR after isolated- venv smoke testing — see [PR #94]):
Package Floor before Floor after Source PR playwright1.56.0 1.59.0 #80 weasyprint61.0 68.1 #78 openpyxl3.1.0 3.1.5 #76 google-api-python-client2.100.0 2.196.0 #77 google-auth-oauthlib1.0.0 1.4.0 #79 All five upper bounds preserved. No CVE-driven escalations.
Caveat:
google-auth-oauthlib1.4.0 drops Python 3.9 support. This repo'spyproject.tomlrequires Python>=3.10already, so no impact for the declared support matrix. External consumers still on 3.9 should pingoogle-auth-oauthlib<1.4.0themselves.
The following items are out of scope for v1.9.9 to keep this a clean patch release. v2 will be a separate design conversation:
- #11 SPA / CSR audit support (7-phase implementation; PR #90 Limitations section remains the patch-appropriate response)
- #51 Subagent research persistence (changes documented output contract
across 15 agent files; v2 will define a persistence convention shared by
seo-audit,seo-drift,seo-cluster) - #61
google_report.py --type fullaudit-schema handling (no regression baseline fixture corpus exists; v2 will ship one with the bug fix) - #89 uv adoption (issue itself labels v2.x candidate; preserves
requirements.txtformat as migration headroom) - #53 seo-notebooklm skill (depends on unofficial wrapper, 536 lines of unreviewed credential code; v2 will define an "experimental skills" tier)
- PR #46 path resolution + macOS SSL:
pip-system-certsis a new dependency that violates v1.9.9's no-new-deps non-goal. v2 will land the full macOS support story.
- No breaking changes. Patch release per SemVer.
- The orchestrator's Sub-Skills numbered list was renumbered (insertion of
seo-content-brief, removal ofseo-firecrawl). Any downstream consumer that referenced sub-skills by index rather than name would break; grep found no such consumer in this repo, but third-party docs that hard-coded "skill 21 is seo-firecrawl" would need updating. /seo auditstill does NOT persist subagent research/findings between runs (this is the intentional v1.x contract; v2 will revisit per #51).
- Skill-count drift returned via PR #56. When the
seo-content-briefskill was merged into v1.9.7 it added a 21st core skill, but the manifest reconciliation in v1.9.7 had locked the canonical phrasing at "20 core" and was not re-run after Phase C. Result: plugin.json, marketplace.json, README.md, CLAUDE.md, AGENTS.md, and docs/ARCHITECTURE.md all under-claimed by one. Reconciled to "25 sub-skills (21 core + 1 orchestrator + 1 framework integration + 2 extension mirrors)".
tests/test_manifest_consistency.py: pytest suite that asserts plugin.json + marketplace.json claimed counts match the actual on-disk count ofskills/*/SKILL.mdandagents/seo-*.md, that plugin.json and marketplace.json descriptions agree on the canonical math, that user-visible docs (README, CLAUDE.md, AGENTS.md) reference the same skill count, and that plugin.jsonversionand CITATION.cffversiontriangulate. Closes the systemic gap that allowed two skill-count drift incidents in v1.9.7.pytest tests/job in.github/workflows/ci.yml: runs the new manifest consistency suite on every push to main and every pull request, gating future skill additions behind matching documentation updates.
uninstall.shanduninstall.ps1now use glob enumeration rather than a hardcoded skill list. The previous scripts had been frozen at v1.4.0-era state and missed 12 sub-skills and 11 sub-agents added between v1.5 and v1.9.8 (seo-backlinks,seo-cluster,seo-content-brief,seo-dataforseo,seo-drift,seo-ecommerce,seo-flow,seo-google,seo-image-gen,seo-local,seo-maps,seo-sxoand the corresponding agents). Anyone who ran the old uninstaller got half a cleanup. Glob enumeration auto-tracks future skill additions without requiring uninstaller maintenance. Sandbox test confirms the new scripts remove everyseoandseo-*skill plus everyseo-*.mdagent while leaving sibling skills (e.g.blog-writer,security) untouched.- This release rolls forward two commits that landed on main after the v1.9.7
tag was cut:
8514999: marketplace metadata polish (addedcategory: "marketing",homepage: https://claude-seo.md, and a 14-keyword array to the marketplace.json plugin entry). Theauthorobject for the plugin entry was intentionally scoped here too but did not land in this commit; it lands in v1.9.9 (issue #92).66a7485: em-dash sweep on user-visible AGENTS.md and CHANGELOG.md Both were intentionally scoped at v1.9.7 but landed post-tag. v1.9.8 captures them properly.
- Skill-count drift across 5 manifests:
plugin.json("20 core sub-skills"),marketplace.json("21 core sub-skills"),CLAUDE.mdline 7 ("21 core sub-skills"),AGENTS.mdline 8 ("20 core sub-skills") + line 84 ("23 skills"), andREADME.mdline 7 ("21 core sub-skills") all contradicted each other. Reconciled to canonical phrasing: "24 sub-skills (20 core + 1 orchestrator + 1 framework integration + 2 extension mirrors)". - Sub-agent count drift:
CLAUDE.mdclaimed "16 core subagents (+ 2 extension agents, 18 total)" whileAGENTS.mdclaimed "15 core subagents (+ 2 extension agents, 17 total)". Reconciled to: "18 sub-agents (15 core + 1 framework integration + 2 extension mirrors)". CLAUDE.mdself-contradiction: line 23 statedplugin.json (v1.9.0); updated to currentv1.9.7.marketplace.jsondescription fields: bothmetadata.description(top-level) andplugins[0].descriptionnow use canonical phrasing.CITATION.cffversion drift: was stuck at1.8.2(six minor versions behind); bumped to matchplugin.jsonat1.9.7with current release date.
.github/dependabot.yml: weekly Dependabot updates for pip and GitHub Actions ecosystems (closes supply-chain hygiene gap).CODE_OF_CONDUCT.md: Contributor Covenant 2.1, closing GitHub Community Standards gap..github/workflows/ci.ymlpermissions:block: restrictsGITHUB_TOKENtocontents: readat workflow root (least-privilege; was previously default scope).
- Patch release driven by repository hygiene + marketplace-readiness preparation. No skill behavior changes, no breaking changes, no script changes.
translations/uk/: the Ukrainian localization (originally contributed by @edocltd in PR #50, shipped in v1.9.0) has been retired. The translation drifted across v1.9.0 to v1.9.7 with no maintenance signal, and a partially translated set is worse than no translation at all when readers cannot tell what is current. @edocltd's contribution remains credited inCONTRIBUTORS.md. If a maintained translation is desired in the future, it should land via a contributor who can commit to keeping it in sync release over release.
- VULN-A01 (HIGH): Removed
Bashfromseo-flowagent tool grant, agent no longer has shell access, eliminating prompt-injection-to-shell attack surface - VULN-A02/A07 (MEDIUM/LOW): Switched
sync_flow.pyto anonymous-first GitHub API requests; PAT only used as 403-triggered fallback, eliminates token-on-redirect leak - VULN-A03 (MEDIUM): Added
Path.resolve()containment check inrecord_write(), blocks path-traversal writes outside the skill reference directory - VULN-A04 (MEDIUM): Introduced
flow-prompts.lockSHA-256 baseline file; sync now diffs against baseline and reports upstream drift before writing - VULN-A05 (MEDIUM): Added explicit "WebFetch is untrusted" security rule to agent body, agent warned not to execute or relay fetched content verbatim
- VULN-A06 (LOW):
ghCLI absence now degrades to anonymous API rather than hard-exiting, sync works without gh CLI on public repos - VULN-A08 (LOW): All file writes are now atomic (tempfile + shutil.move), eliminates partial-write corruption on interrupt
- VULN-A09 (LOW): GitHub API responses capped at 5 MB with 15s timeout, prevents memory exhaustion from malformed or oversized API payloads
- VULN-A10 (LOW): URL allowlist validates every request targets
api.github.comover HTTPS, blocks SSRF ifAPI_ROOTconstant is modified - INFO-A14: Added CC BY 4.0 attribution header to
references/prompts/README.md
- Added 10 new unit/integration tests covering all above findings
- Test count: 5 → 15
- seo-flow: FLOW framework integration, Find → Leverage → Optimize → Win. 41 evidence-led AI prompts (CC BY 4.0) bundled as
skills/seo-flow/references/prompts/(find:5, leverage:1, optimize:21, win:3, local:11). Commands:/seo flow [find|leverage|optimize|win|local|prompts|sync]. - Context-matching orchestration:
/seo flow optimizeselects 2-3 most relevant prompts from 21 based on URL industry signals and prior skill output, not a full dump. scripts/sync_flow.py: GitHub API sync script, pulls latest FLOW prompts, framework doc, and bibliography from AgriciDaniel/flow. Supports--dry-runand--ref <sha>pinning. Outputs JSON summary.agents/seo-flow.md: FLOW subagent, applies stage prompts to target URLs, returns structured evidence-tagged findings.- FLOW cross-references: Integration notes added to seo-geo, seo-local, seo-content, and seo-cluster skills.
- FLOW content bundled under CC BY 4.0. Attribution header on every prompt file (automated by
sync_flow.py). Claude SEO's MIT license unchanged, applies to skill code only.
- seo-cluster: SERP-based semantic topic clustering for content architecture (skill + 3 references + interactive cluster-map.html visualization + agent). Contributed by Lutfiya Miller (Pro Hub Challenge Winner).
- seo-sxo: Search Experience Optimization, reads SERPs backwards to detect page-type mismatches, derives user stories, scores pages from persona perspectives (skill + 4 references + agent). Contributed by Florian Schmitz.
- seo-drift: SEO drift monitoring, baseline, diff, and track changes to on-page SEO with 17 comparison rules across 3 severity levels. SQLite persistence (skill + 1 reference + agent + 4 Python scripts). Contributed by Dan Colta. Security-hardened: all curl usage eliminated, SSRF protection enforced.
- seo-ecommerce: E-commerce SEO, Google Shopping intelligence, Amazon marketplace analysis, product schema validation (skill + 1 reference + agent + 2 Python scripts). Contributed by Matej Marjanovic.
- DataForSEO cost guardrails:
scripts/dataforseo_costs.pywith threshold-based approval, session budget tracking, daily spend summaries.references/cost-tiers.mdpricing table. Contributed by Matej Marjanovic. - seo-hreflang cultural profiles: 4 cultural adaptation profiles (DACH, Francophone, Hispanic, Japanese) with locale format tables, content parity audit, and freshness tracking. 3 new reference files. Contributed by Chris Muller.
- CONTRIBUTORS.md: Community credits file for Pro Hub Challenge and PR contributors
- AGENTS.md: Multi-platform discovery file for Cursor/Antigravity (concept by Matej Marjanovic, rewritten for v1.9.0)
- Schema templates: Product (Full E-commerce) and ItemList (hub/pillar pages) added to
schema/templates.json - 5 new commands:
/seo cluster,/seo sxo,/seo drift baseline|compare|history,/seo ecommerce
- Orchestrator spawns up to 15 subagents (was 12): +seo-cluster, +seo-sxo, +seo-drift, +seo-ecommerce
- seo-hreflang SKILL.md enhanced with Cultural Adaptation Assessment, Content Parity Audit, and Locale Format Validation sections
- seo-dataforseo SKILL.md enhanced with Cost Guardrails section requiring cost checks before API calls
- All 23 SKILL.md files stamped to v1.9.0
- Install scripts (install.sh, install.ps1) pinned to v1.9.0
- plugin.json updated with 9 new keywords
- Pro Hub Challenge: Lutfiya Miller (Winner - Semantic Cluster Engine), Florian Schmitz (SXO Skill), Dan Colta (SEO Drift Monitor), Chris Muller (Multi-lingual SEO), Matej Marjanovic (E-commerce + Cost Config + Platform Support), Benjamin Samar (SEO Dungeon - reviewed)
- 5 out of 6 submissions scored Proficient or above
- See CONTRIBUTORS.md for full credits and original repo links
- Ukrainian localization: first i18n, README, CONTRIBUTING, PRIVACY, SECURITY, INSTALLATION, TROUBLESHOOTING translated (PR #50)
- Firecrawl extension section in README with install and example commands
- Backlink API privacy disclosures in PRIVACY.md (Moz, Bing Webmaster, Common Crawl, verify crawler)
- 4 missing commands added to README table:
/seo backlinks,/seo firecrawl,/seo dataforseo,/seo image-gen - 6 missing scripts added to CI syntax check (backlinks_auth, moz_api, bing_webmaster, commoncrawl_graph, verify_backlinks, validate_backlink_report)
- 6 missing skill directories added to INSTALLATION.md manual uninstall list
- Install scripts pinned to stale version: REPO_TAG bumped from v1.7.2 to v1.8.2 in install.sh and install.ps1, new curl-based installs now get the current release
- Supply chain risk in docs: removed deprecated
irm | iexpattern from docs/INSTALLATION.md, replaced with safegit clone+powershell -Filemethod - Version sync: pyproject.toml (1.7.2→1.8.2), CITATION.cff (1.7.2→1.8.2, date 2026-04-10), all 19 SKILL.md files
- Python requirement: pyproject.toml corrected from
>=3.11to>=3.10(matches README and install scripts) - README architecture counts: sub-skills "15+2" → "16+3", agents "10+2" → "11+2"
- Orchestrator SKILL.md: stale count "15+2" → "16+3" at line 119
- CLAUDE.md: sub-skill count 17→16 core, script count "20+2" → "21+2"
- Extension install hang: merged PR #43, npx pre-warm no longer starts MCP server binary
- Merged PR #43 (fix stuck extension install) by @olivierroy
- Merged PR #45 (correct sub-skills count) by @MalteBerlin
- Merged PR #50 (Ukrainian localization) by @edocltd
- Closed issue #42 (marketplace discovery, resolved)
- Reviewed PRs #47, #46, #30, #36 with detailed feedback
- Google Images SERP:
/seo dataforseo serp-images <keyword>command for competitive image search analysis - Image SERP Analysis:
/seo images serp <keyword>cross-skill command combining DataForSEO image results with on-page audit - Image File Optimization:
/seo images optimize <path>for WebP/AVIF conversion, IPTC/XMP metadata injection, responsive variants, and compression - Image ranking factors table: documents what matters (alt text, filename, page context) vs what does not (EXIF camera data, IPTC keywords)
- DataForSEO field-config:
serp.items.imagesfilter with 10 SEO-relevant fields (type, rank, title, alt, url, source_url, image_url, domain, encoded_url) - Tool catalog reference:
skills/seo-dataforseo/references/tool-catalog.mdfor 35+ utility MCP tools (moved from inline list) - Table of Contents: added to
seo-image-gen/references/prompt-engineering.md(326 lines, per >300 line standard) - Plugin keywords:
image-serp,google-imagesadded to plugin.json
- Version mismatch: unified all 19 SKILL.md files, plugin.json, and CLAUDE.md to v1.8.0 (was 1.7.0/1.7.2/1.8.0 three-way split)
- Broken reference path: seo-backlinks now correctly points to
skills/seo/references/backlink-quality.md(shared reference) - Hardcoded absolute paths: removed
~/.claude/skills/fromagents/seo-visual.md,agents/seo-schema.md,skills/seo-image-gen/SKILL.md, and banana extension copy (now use plugin-relative paths) - seo-dataforseo line count: moved 35-line utility tools list to reference file, reduced from 416 to 380 lines
- seo-images description: added trigger phrases for image SERP, metadata, WebP conversion
- seo-dataforseo description: added "Google Images" and image ranking trigger phrases
- seo orchestrator: updated images command to reflect new SERP + optimize capabilities
- CLAUDE.md: updated plugin version reference, images command description
- Firecrawl extension: Full-site crawling, scraping, and site mapping via Firecrawl MCP (
extensions/firecrawl/)- 4 commands: crawl, map, scrape, search
- JS rendering support for SPA/CSR sites (addresses #11)
- Cross-skill integration with audit, technical, sitemap, and content skills
- Self-contained install/uninstall scripts (Bash + PowerShell)
- Backlink analysis skill:
skills/seo-backlinks/SKILL.mdwith/seo backlinkscommand- 7-section analysis: profile overview, anchor text, referring domain quality, toxic links, top pages, competitor gap, new/lost links
- Backlink health score (0-100) with weighted factors
- Disavow recommendations with export format
- Requires DataForSEO extension for live data
- Backlink quality reference:
skills/seo/references/backlink-quality.mdwith 30 toxic link patterns, anchor text benchmarks by industry - Excel export:
--format xlsxoption inscripts/google_report.py- Sheets: Summary, Queries, Pages, Indexation (conditional on data available)
- Navy header styling matching PDF palette, auto-column-width, frozen headers, auto-filter
- New format options:
xlsx,all(pdf+html+xlsx)
- Ecosystem cross-links: AI Marketing Claude added to README and CLAUDE.md ecosystem sections
- Sub-skill count: 18 -> 19 (added seo-backlinks)
- Extension count: 2 -> 3 (added Firecrawl)
- Orchestrator routing table updated with
/seo backlinksand/seo firecrawlcommands - Audit orchestration: Firecrawl
mapused for URL discovery when available requirements.txt: addedopenpyxl>=3.1.0for Excel export
- install.sh: broken skill copy path
seo/corrected toskills/seo/(h/t @hieu-e via #39) - install.sh: version tag pinned to v1.7.1 (was stuck at v1.6.0)
- install.ps1: version tag pinned to v1.7.1 (was stuck at v1.6.0)
- install.ps1: removed unnecessary
seo/fallback path, usesskills\seodirectly
- CI: syntax check expanded from 4 to 15 Python scripts (all v1.7.0 Google API scripts now covered)
- Google SEO APIs skill:
skills/seo-google/SKILL.mdwith 21 commands across 4 credential tiers - Google subagent:
agents/seo-google.mdfor enriched audit data (CWV field data, indexation status, organic traffic) - 11 Python scripts: google_auth.py, gsc_query.py, gsc_inspect.py, pagespeed_check.py, crux_history.py, indexing_notify.py, ga4_report.py, google_report.py, youtube_search.py, nlp_analyze.py, keyword_planner.py
- 10 reference files: auth-setup.md, search-console-api.md, pagespeed-crux-api.md, indexing-api.md, ga4-data-api.md, youtube-api.md, nlp-api.md, keyword-planner-api.md, supplementary-apis.md, rate-limits-quotas.md
- PDF report generator:
scripts/google_report.pywith enterprise A4 template, WeasyPrint + matplotlib charts, post-generation quality review - OAuth web credential flow: Browser-based auth with localhost:8085 callback, token refresh, manual code exchange fallback
- 4-tier credential system: Tier 0 (API key: PSI/CrUX), Tier 1 (+OAuth/SA: GSC/Indexing), Tier 2 (+GA4), Tier 3 (+Ads Keyword Planner)
- Python dependencies: google-api-python-client, google-auth, google-auth-oauthlib, google-auth-httplib2, google-analytics-data, matplotlib, weasyprint
- SSRF protection:
validate_url()blocks private IPs, loopback, and GCP metadata endpoints in all Google API scripts .gitignorehardened with 8 credential patterns:.env,client_secret*.json,oauth-token.json,service_account*.json- OAuth tokens no longer store
client_secret(reads from client_secret.json file only) - Removed hardcoded user paths from all scripts (mobile_analysis.py, capture scripts)
- Sub-skill count: 14 -> 15 core (+ 2 extensions)
- Subagent count: 9 -> 10 core (+ 2 extension) with conditional Google API spawning
- seo-audit spawns seo-google agent when Google API credentials detected
- seo-technical and seo-performance can use CrUX field data when available
- Report Generation Rules added to CLAUDE.md with color palette, dependency, and cross-skill enforcement
- README updated with Google APIs, local SEO, maps, and PDF report features
- Marketplace distribution: Created
.claude-plugin/marketplace.jsonfor plugin marketplace submission. Users can now install via/plugin marketplace add AgriciDaniel/claude-seo - Agent model and turn limits: All 11 subagents now specify
model: sonnetandmaxTurns(15-25) for predictable cost and behavior - Plugin keywords: Added 12 discovery keywords to
plugin.jsonfor marketplace searchability
- Standard directory structure: Moved
seo/orchestrator toskills/seo/for auto-discovery compliance. Extension skills (seo-dataforseo, seo-image-gen) and agents copied to standardskills/andagents/directories - plugin.json rewrite: Removed non-standard
entry_pointfield and individual file-path arrays forskills/agents. All 17 skills and 11 agents now rely on directory auto-discovery per Anthropic plugin spec - allowed-tools format: Converted from YAML arrays to comma-separated strings across all 17 SKILL.md files
- Metadata standardized: Added
license: MITandmetadata:block (author, version, category) to all SKILL.md frontmatters - Cross-references: Updated all agent and skill files referencing
seo/references/toskills/seo/references/ - CLAUDE.md: Architecture tree updated to reflect new structure
- Plugin validation:
claude plugin validate .now passes cleanly (previously would fail on non-standard fields)
- Local SEO skill:
skills/seo-local/SKILL.mdfor GBP, NAP, citations, reviews, and map pack analysis - Maps intelligence skill:
skills/seo-maps/SKILL.mdfor geo-grid rank tracking, GBP auditing, review intelligence, competitor radius mapping - Maps subagent:
agents/seo-maps.mdfor parallel maps analysis during audits - Local subagent:
agents/seo-local.mdfor parallel local SEO analysis - Maps reference files: 4 new reference files (maps-geo-grid.md, maps-gbp-checklist.md, maps-api-endpoints.md, maps-free-apis.md)
- Local reference files: 2 new reference files (local-seo-signals.md, local-schema-types.md)
- Installer fixes: Cross-platform install script improvements
- Subagent count: 7 -> 9 core (+ 2 extension) with conditional local/maps spawning
- Sub-skill count: 12 -> 14 core (+ 2 extension)
- Frontmatter fields:
user-invokable,argument-hint, andallowed-toolsadded to all SKILL.md files per Anthropic best practices - Error handling sections: Added to all SKILL.md files with skill-specific guidance
- Plugin manifest:
.claude-plugin/plugin.jsonupdated with all skills and agents registered - Version tracking:
pyproject.tomlwith project metadata
- Em dash elimination: Replaced em dashes (U+2014) across files with appropriate punctuation (colons, commas, semicolons, periods) to reduce AI detection signals
- HTML comments before frontmatter: Removed
<!-- Updated: ... -->comments from SKILL.md files that preceded the YAML frontmatter delimiter - Anthropic compliance audit: Full audit against official skill-building guidelines, all checks now pass
- Technical SEO: Updated from "8 categories" to "9 categories" in description (IndexNow added in prior update)
- Install script supply chain fix: Replaced
irm | iexWindows PowerShell one-liner withgit clone + powershell -Fileas primary install method. Claude Code's own security guardrails flagged the old pattern as a supply chain risk (reported by community member). Added collapsible "review before running" section for Unix curl method. - Version pinning:
install.shandinstall.ps1now clone a specific release tag (v1.3.0) by default rather thanmain, preventing silent updates. Override withCLAUDE_SEO_TAG=main. - PowerShell Invoke-External hardening: Comprehensive
PSNativeCommandUseErrorActionPreferencehandling inInvoke-Externalwrapper (fixes Windows git clone stderr false-positive termination, from PR #13 + PR #15).
- GEO agent deployed:
agents/seo-geo.mdcreated --/seo auditnow spawns 7 parallel agents (was 6). GEO analysis covers AI crawler access, llms.txt, passage-level citability, brand mention signals, platform-specific scoring (Google AI Overviews, ChatGPT, Perplexity, Bing Copilot). --googlebotflag infetch_page.py: Detect prerender/dynamic rendering services by comparing response size with default UA vs Googlebot UA. First phase of SPA/CSR support (Issue #11).
- URL normalization:
capture_screenshot.pyandanalyze_visual.pynow accept bare domains (example.com->https://example.com) via sharednormalize_url()helper (from PR #16 by @shuofengzhang). - GEO weight: AI Search Readiness weight increased from 5% to 10% in overall SEO Health Score. Technical SEO adjusted to 22%, Content Quality to 23%.
- FAQPage guidance: Blanket "remove FAQPage on commercial sites" updated to nuanced guidance -- existing FAQPage -> Info priority (not Critical), noting AI/LLM citation benefit. Adding new FAQPage -> not recommended for Google, note AI benefit. Updated in
seo/SKILL.md,agents/seo-schema.md,seo/references/schema-types.md. - Uninstall agents list: Added
seo-geotouninstall.shanduninstall.ps1removal lists. - Python requirement: Corrected from
3.8+to3.10+inREADME.mdanddocs/INSTALLATION.md.
- Subagent count: 6 -> 7 (added seo-geo to core audit pipeline)
.gitignore: Added generated audit artifacts (charts/, PDFs, report.html, firebase-debug.log, generated-schema.json)
- Extension system:
extensions/directory convention for self-contained add-ons with install/uninstall scripts - DataForSEO extension: 22 commands across 9 API modules (SERP, keywords, backlinks, on-page, content, business listings, AI visibility, LLM mentions). Install:
./extensions/dataforseo/install.sh - DataForSEO integration: seo-audit, seo-content, seo-geo, seo-page, seo-plan, seo-technical auto-detect DataForSEO MCP tools for enriched analysis
- Plugin manifest:
.claude-plugin/plugin.jsonfor official plugin directory submission - Documentation: Extensions architecture in ARCHITECTURE.md, 22 new commands in COMMANDS.md, updated MCP integration guide
- Title tag threshold: Pre-commit hook now uses 60-char max, aligned with quality-gates.md and echo message
- SSRF prevention: Added to
capture_screenshot.py(defense-in-depth, matchingfetch_page.py) - Frontmatter cleanup: Removed non-standard
allowed-toolsfrom main SKILL.md
- Sub-skill count: 12 + 1 extension (added seo-dataforseo via DataForSEO extension)
- Subagent count: 6 + 1 optional (added seo-dataforseo agent via extension)
- DataForSEO promoted from "Community" to "Official extension" in MCP docs
- User-Agent header: Changed default from bot-style
ClaudeSEO/1.0to Chrome-like string withClaudeSEO/1.2suffix. SSR frameworks (Next.js, Nuxt, Angular) now pre-render properly instead of serving empty client-side shells (#9) - Custom User-Agent support: Added
--user-agentflag tofetch_page.pyfor configurable UA strings
- install.cat support: Added alternative install method via
curl install.cat/AgriciDaniel/claude-seo | bashto README (#10)
- SSRF prevention: Added private IP blocking to
fetch_page.pyandanalyze_visual.py - Path traversal prevention: Added output path sanitization to
capture_screenshot.pyand file validation toparse_html.py - Install hardening: Removed
--break-system-packages, switched to venv-based pip install - requirements.txt: Now persisted to
~/.claude/skills/seo/for user retry
- YAML frontmatter parsing: Removed HTML comments before
---delimiter in 8 files (skills: seo-content, seo-images, seo-programmatic, seo-schema, seo-technical; agents: seo-content, seo-performance, seo-technical). Thanks @kylewhirl for identifying this in the codex-seo fork. - Windows installer: Merged @kfrancis improvements --
python3 -m pip,py -3launcher fallback, requirements.txt persistence, non-fatal subagent copy, better error diagnostics (PR #6) - requirements.txt missing after install: Now copied to skill directory so users can retry (#1)
- Python dependencies now installed in a venv at
~/.claude/skills/seo/.venv/with--userfallback (#2) - Playwright marked as explicitly optional in install output
- Windows installer uses
Resolve-Pythonhelper for robust Python detection (#5)
- urllib3 >=2.6.3: Fixes CVE-2026-21441 (CVSS 8.9) - decompression bypass vulnerability
- lxml >=6.0.2: Updated from 5.3.2 for additional libxml2 security patches
- Pillow >=12.1.0: Fixes CVE-2025-48379
- playwright >=1.55.1: Fixes CVE-2025-59288 (macOS)
- requests >=2.32.4: Fixes CVE-2024-47081, CVE-2024-35195
- GEO (Generative Engine Optimization) major enhancement:
- Brand mention analysis (3x more important than backlinks for AI visibility)
- AI crawler detection (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot, etc.)
- llms.txt standard detection and recommendations
- RSL 1.0 (Really Simple Licensing) detection
- Passage-level citability scoring (optimal 134-167 words)
- Platform-specific optimization (Google AI Overviews vs ChatGPT vs Perplexity)
- Server-side rendering checks for AI crawler accessibility
- LCP Subparts analysis: TTFB, resource load delay, resource load time, render delay
- Soft Navigations API detection for SPA CWV measurement limitations
- Schema.org v29.4 additions: ConferenceEvent, PerformingArtsEvent, LoyaltyProgram
- E-commerce schema updates: returnPolicyCountry now required, organization-level policies
- E-E-A-T framework: Updated for December 2025 core update - now applies to ALL competitive queries, not just YMYL
- SKILL.md description: Expanded to leverage new 1024-character limit
- Schema deprecations expanded: Added ClaimReview, VehicleListing (June 2025)
- WebApplication schema: Added as correct type for browser-based SaaS (vs SoftwareApplication)
- Schema-types.md now correctly distinguishes SoftwareApplication (apps) vs WebApplication (SaaS)
- Initial release of Claude SEO
- 9 specialized skills: audit, page, sitemap, schema, images, technical, content, geo, plan
- 6 subagents for parallel analysis: seo-technical, seo-content, seo-schema, seo-sitemap, seo-performance, seo-visual
- Industry templates: SaaS, local service, e-commerce, publisher, agency, generic
- Schema library with deprecation tracking:
- HowTo schema marked deprecated (September 2023)
- FAQ schema restricted to government/healthcare sites only (August 2023)
- SpecialAnnouncement schema marked deprecated (July 31, 2025)
- AI Overviews / GEO optimization skill (seo-geo) - new for 2026
- Core Web Vitals analysis using current metrics:
- LCP (Largest Contentful Paint): <2.5s
- INP (Interaction to Next Paint): <200ms - replaced FID on March 12, 2024
- CLS (Cumulative Layout Shift): <0.1
- E-E-A-T framework updated to September 2025 Quality Rater Guidelines
- Quality gates for thin content and doorway page prevention:
- Warning at 30+ location pages
- Hard stop at 50+ location pages
- Pre-commit and post-edit automation hooks
- One-command install and uninstall scripts (Unix and Windows)
- Bounded Python dependency pinning with CVE-aware minimums (lxml >= 5.3.2)
- Follows Anthropic's official Claude Code skill specification (February 2026)
- Standard directory layout:
scripts/,references/,assets/ - Valid hook matchers (tool name only, no argument patterns)
- Correct subagent frontmatter fields (name, description, tools)
- CLI command is
claude(notclaude-code)