All notable changes to Toprank will be documented here.
The format follows Keep a Changelog.
- ads-audit SKILL.md slimmed from ~600 to ~140 lines — Stripped phase-by-phase narrative, report markdown templates, output-discipline rules, per-dimension prose, and the conditional-handoff table. Modern models handle structure and formatting on their own; the skill no longer needs to spell that out.
- Kept the durable encoded judgment — Audit response shape, field→dimension scoring map, 0–5 score rubric, Impression Share Interpretation Matrix, the agency-earned heuristics (weighted QS by spend, brand leakage 5–10× CPA premium, waste formula, Display+Search mixing, STOP condition on conversion tracking), policy freshness check, and the filesystem contract (
business-context.json+personas/{accountId}.jsonschemas). - Tightened guardrails — Explicit "read-only skill, mutations go through
/ads" rule at top and bottom; STOP condition for broken conversion tracking; mandate to always persist business-context and personas even on short reports.
- Google Ads MCP detection on Claude Desktop plugin — When connected through the claude.ai plugin connector, the AdsAgent MCP server is exposed as
mcp__claude_ai_AdsAgent__*instead ofmcp__adsagent__*. The shared preamble only recognized the Claude Code CLI prefix, so ads-audit and other google-ads skills incorrectly reported "MCP not connected" and refused to run. Detection now scans the available tool list for any*listConnectedAccountstool, extracts the prefix, and uses that prefix for all subsequent tool calls. Supports Claude Code CLI, Claude Desktop, and any future host using an AdsAgent variant.
- Consistent tool references across google-ads skills —
ads-copy/SKILL.mdhad hardcodedmcp__adsagent__prefixes on tool references (createAd,updateAdAssets,enableAd,undoChange,listAds,pauseAd,listCampaigns,listAdGroups). Stripped to bare tool names to matchads,ads-audit, andads-landing, which already used bare names. Skills now uniformly defer to the preamble for prefix resolution.
- Change impact review mode — Users can now say "check my changes" or "did my changes work" to review the impact of recent Google Ads changes. The ads skill routes these requests through session-checks with proper before/after metric comparison
- Maturation guidance — When changes haven't had enough time to accumulate data (7 days for bid/keyword changes, 14 days for structural changes), the skill explains why and tells the user when to check back instead of showing misleading early metrics
- Deduped headline/description formulas — Removed 50-line inline formula table from ads-copy SKILL.md; now references
rsa-best-practices.mdas single source of truth - Deduped business context intake — Removed 75-line duplicate intake procedure from ads-copy SKILL.md; now references
ads-audit/references/business-context.mdas canonical source - Fixed session-checks query logic — Changed from filtering only matured entries to finding all unreviewed entries, then branching on maturation status. Previously the "still maturing" message could never fire
- Removed overly generic triggers — Dropped "did it improve" and "what happened after" which could false-positive on non-ads queries
- Ads skill SKILL.md slimmed down — Extracted analysis heuristics, change tracking, session checks, and workflow playbooks into dedicated reference files under
google-ads/ads/references/. Main SKILL.md went from ~770 lines to ~160 lines, loading references on demand instead of carrying everything inline - New reference routing table — SKILL.md now has a quick-lookup table mapping situations (performance analysis, QS diagnostics, bid strategy, etc.) to the right reference file
- Added new MCP tools — Listed
getKeywordIdeas,getPmaxAssetGroups,getPmaxAssets,searchGeoTargets,updateCampaignBidding,updateCampaignGoals,removeAd,pausePmaxAssetGroup,enablePmaxAssetGroupin the tool catalog - Two interaction modes — SKILL.md now distinguishes direct actions (fast path, no session checks) from analysis requests (full checks and reporting)
- Dev symlink detection —
bin/toprank-update-checkand the upgrade skill now detect dev symlinks and skip update checks, preventing upgrade attempts on developer-managed installs
- Ads audit restructured around 5 analysis layers and 3 actionable passes — Replaced the 7-dimension scoring model (0-5 per dimension, 0-100 composite) with a layered analysis approach: Signals → Relevance → Efficiency → Scale → Growth. Findings now map to 3 action-oriented passes: Stop Wasting, Capture More, Fix Fundamentals. Tracks 3 objective pulse metrics (waste rate, demand captured, CPA) with trend comparison on re-audits
- Added PMax campaign support — Asset group completeness checks, PMax cannibalization detection for brand Search traffic, and PMax-specific bid strategy guidance
- Added audience signals check — Flags Search campaigns missing audience segments and PMax asset groups without audience signals, both of which limit Smart Bidding performance
- Smarter waste calculation — Keyword waste threshold changed from "clicks > 10" to "spend > 2x account average CPA", which respects Smart Bidding's learning curve. Added de-duplication rules to prevent double-counting between keyword and search term waste
- On-demand reference loading — Quality score, search term analysis, industry benchmarks, and campaign structure references now load only when relevant issues are detected, saving ~1,000 lines of context per audit
- Extracted reference docs — Persona discovery template and business context crawl procedure moved to dedicated reference files for reuse across skills
- Gemini cross-model review skill — New
/toprank:geminiskill that launches Google's Gemini CLI as an independent reviewer. Three modes: review (pass/fail gate), challenge (adversarial stress test), and consult (open Q&A). Unlike code-only review tools, handles Google Ads changes (campaign structure, bid strategies, keywords) and SEO metadata changes (title tags, meta descriptions, schema markup) alongside code diffs. Produces cross-model analysis when Claude has already reviewed the same changes, highlighting overlapping findings, unique catches, and disagreements.
- Consolidated root files — Merged
CONNECTORS.mdandCONTRIBUTING.mdintoREADME.mdto reduce file count without losing content. Connectors section now lives under "## Connectors" and contributing guide under "## Contributing" in the main README. - Updated install test — Test suite now checks for connector content in
README.mdinstead of the removedCONNECTORS.mdfile. - Added
seo-pageto directory tree in README to reflect the new single-page analysis skill.
CONNECTORS.md— Content moved to README.md.CONTRIBUTING.md— Content moved to README.md.requirements-test.lock— Stale lock file that didn't matchrequirements-test.txt.
- PageSpeed Insights API integration — New
pagespeed.pyscript calls the Google PageSpeed Insights API for multiple URLs with concurrent execution. Collects Lighthouse performance scores, Core Web Vitals (LCP, INP, CLS, FCP, TTFB), CrUX field data, optimization opportunities, and diagnostics. Supports both mobile and desktop strategies. - PageSpeed display script — New
show_pagespeed.pyrenders PageSpeed results in a terminal-friendly format with scores, metrics, opportunities, and diagnostics. - Phase 5.5 in SEO analysis — PageSpeed analysis runs in parallel alongside URL Inspection and CMS detection during audits. Results feed into the report with a dedicated "PageSpeed & Core Web Vitals" section and are logged in the audit history for tracking performance over time.
- Preflight PageSpeed checks —
preflight.pynow auto-enables the PageSpeed Insights API and checks for aPAGESPEED_API_KEYin the environment or~/.toprank/.env.
- API key check moved to shared preamble — Moved the
ADSAGENT_API_KEYverification from the/adsskill into the shared preamble so all google-ads skills (/ads,/ads-audit,/ads-copy) verify the key automatically. The key is saved to~/.claude/settings.jsonunderenv(not config files) since the MCP server reads it from the environment. - Preamble rewrite — Clean step numbering (0–5), fixed MCP detection to always run (was skipped for returning users with saved accountId), eliminated duplicate
listConnectedAccountscalls, explicit deep-merge instructions for settings.json to avoid clobbering existing env vars. - Removed
apiKeyfrom config schema — Config files (.adsagent.json,config.json) now only storeaccountId. API key storage is exclusively in~/.claude/settings.json.
- API key verification gate — The
/adsskill now checks forADSAGENT_API_KEYin~/.claude/settings.jsonbefore executing any other step. If the key is missing, it prompts the user to obtain one from adsagent.org, collects the key interactively, and saves it automatically.
- Plugin-aware auto-upgrade — Rewrote the upgrade system from the old
~/.claude/skills/paths to the new~/.claude/plugins/cache/plugin model. The upgrade flow now updates the marketplace repo, copies to a versioned cache directory, and updatesinstalled_plugins.jsondirectly. - Preamble script discovery — Both
bin/preamble.mdandgoogle-ads/shared/preamble.mdnow findtoprank-update-checkvia glob in the plugin cache instead of hardcoded skill paths.
- Legacy skill paths — Dropped all references to
~/.claude/skills/toprank/,~/.claude/skills/stockholm/, and the./setupscript that no longer exists in the plugin model.
- Business relevance gate for keyword evaluation — The
/adsskill now classifies keywords into Tier 1 (Core), Tier 2 (Adjacent), and Tier 3 (Irrelevant) before applying performance heuristics. Core keywords that directly describe the business are never paused — they get a diagnostic workflow instead. - Statistical significance gate — Conversion-based decisions now require expected conversions >= 3 before acting. Prevents false negatives from small sample sizes.
- Core Keyword Diagnostic workflow — 6-step diagnostic for underperforming Tier 1 keywords: statistical significance, sibling comparison, match type analysis, QS subcomponents, position/impression share, and optimization recommendations.
- Wasted spend calculation excludes Tier 1 (Core) keywords. A core keyword with 0 conversions is an optimization opportunity, not waste.
- Bid optimization and waste audit workflows updated to classify keywords by business relevance before applying performance actions.
- MCP server URL updated —
adsagentMCP now points tohttps://adsagent.org/api/mcp(removedwww.prefix to match the canonical domain).
seo-analysisreport restructured for clarity — Phase 6 now leads with "Top Priority Actions" (3–5 items, ordered by expected click impact) instead of 12+ equal-weight sections. Each action requires a specific URL, specific metric as evidence, and a copy-paste-ready fix with estimated click impact. Supporting data (indexing issues, cannibalization, gaps, schema, technical) is condensed into reference tables shown only when findings exist.seo-analysisaudit history tracking — new Step 0.5 reads~/.toprank/audit-log/<domain>.jsonat startup to surface previously flagged issues and their current resolution status. New Phase 6.5 writes a concise log entry after each audit (date, traffic snapshot, top issues with metrics and expected impact). Future audits show which prior issues are resolved, improved, or still open.
seo-analysisPhase 3.7 — removed duplicate$DOMAINextraction; now reuses the variable set in Step 0.5 instead of re-deriving it.
- SEO business context (
seo/shared/business-context.md) — persistent per-domain business profile for SEO skills. Caches business name, summary, industry, primary goal, target audience, locations, brand terms, competitors, and key topics at~/.toprank/business-context/<domain>.json. Fresh for 90 days; auto-refreshes when stale. seo-analysisPhase 3.8 — business context generation after GSC data is collected. First run asks 3 targeted questions and infers the rest from GSC + homepage. Subsequent runs are silent (cache load only).seo-analysisPhase 2 fast-path — brand terms are loaded from business context cache when available, skipping the manual question entirely.- Phase 6 report Business Profile section — report now opens with a business context block (name, goal, audience, competitors) so all recommendations read as contextual rather than generic.
- Config resolution — replaced single global config (
~/.adsagent/config.json) with 3-tier chain: project-level (.adsagent.json), Claude project-level (~/.claude/projects/{path}/adsagent.json), and global fallback. Fields merge up the chain so a project file with onlyaccountIdinheritsapiKeyfrom global. - Project-scoped data storage — when a project-level config exists, data files (business-context, personas, change-log, account-baseline) are stored in
.adsagent/relative to the project root instead of globally - Account switching — now asks whether to save the selection for the current project or globally
- Security — preamble instructs LLM to add
.adsagent.jsonand.adsagent/to.gitignorewhen using project-local storage
- Google Ads Setup section in README — two-path install guide: Option A (free hosted server via adsagent.org) and Option B (self-hosted MCP server for users with their own Google Ads API access)
- Collapsible manual MCP config block for users who skip the setup script
/adsskill — MCP is now the only tool-calling method. Removed mcporter CLI fallback andmcporter.jsonconfig file. The "Calling tools" section now documents themcp__adsagent__<toolName>pattern directly.setup— MCP server config is built inline instead of reading frommcporter.json. Ads skill detection uses directory prefix instead of file existence check. Fixed Windows path compatibility for ads skill detection.- README hook questions — rewritten to target real pain points: wasted ad spend, traffic drops, and conversion growth without budget increases
google-ads/ads/mcporter.json— no longer needed; MCP server config is generated directly by the setup script._replace_key()helper in setup — was only used for mcporter placeholder substitution.
/setup-cmsskill — interactive wizard to connect WordPress, Strapi, Contentful, or Ghost. Detects existing config, collects credentials, tests the connection, and writes to.env.local.- WordPress CMS integration (Phase 3.6) —
preflight_wordpress.py+fetch_wordpress_content.py. REST API with Application Password auth. Extracts SEO fields from Yoast SEO (yoast_head_json) or RankMath (meta.rank_math_title). - Contentful CMS integration (Phase 3.6) —
preflight_contentful.py+fetch_contentful_content.py. Delivery API with Bearer token auth. Resolves linked SEO component entries (include=1), supports pagination up to 1000 entries/page. - Ghost CMS integration (Phase 3.6) —
preflight_ghost.py+fetch_ghost_content.py. Content API with auto-detection between v4+ (/ghost/api/content/) and v3 (/ghost/api/v3/content/). Uses nativemeta_title/meta_descriptionfields. cms_detect.py— lightweight CMS routing script. Checks env vars in priority order (WP_URL → Contentful → Ghost → Strapi), exits 0 with CMS name if found, exits 2 if none configured.- 56 unit tests (
test/unit/test_cms_scripts.py) covering SEO field extraction, entry normalisation, SEO audit aggregation, SSRF protection, and WordPress auth header encoding across all 4 CMSes.
seo-analysisPhase 3.6 — rewritten from Strapi-specific to CMS-agnostic. Now routes throughcms_detect.pyand runs the appropriate preflight + fetch script viacasestatement. All CMSes produce the same normalized JSON format.- Report template: "Strapi SEO Field Audit" → "CMS SEO Field Audit" (supports WordPress, Strapi, Contentful, Ghost).
- Ghost/WordPress/Contentful false negatives — SEO extraction no longer falls back to the content title when no explicit meta title is set. Entries with no SEO plugin / no meta title override are now correctly flagged as
missing_meta_title=True. - Ghost
detect_api_pathsys.exit trap — replacedghost_get()probe (which callssys.exit(1)on errors) with inlineurllibprobe, allowing the v3 API path fallback to actually run. - Ghost
PAGE_SIZE— changed from 15 (display default) to 100 (actual API max).
seo-analysis— GSC display crash — addedshow_gsc.pydisplay utility to replace fragile inline Python scripts. FixesTypeError: string indices must be integers, not 'str'that occurred when iteratingcomparisondict fields (which mixes string metadata and list data at the same level). Also fixes CTR being displayed as 474% instead of 4.74% (was being multiplied by 100 twice).
seo-analysis— URL-first flow — Step 0 now asks for the target website URL before running any preflight or API calls. The URL is stored and used throughout the entire audit for URL Inspection, technical crawl, and metadata fetching.seo-analysis— URL Inspection API (Phase 3.5) — newurl_inspection.pyscript callsPOST https://searchconsole.googleapis.com/v1/urlInspection/index:inspectfor the top pages. Returns per-page indexing status (INDEXED,NOT_INDEXED,DUPLICATE_WITHOUT_CANONICAL, etc.), mobile usability verdict, rich result status, last crawl time, and referring sitemaps. Results surface immediately as critical flags in the report.seo-analysis— Keyword Gap Analysis (Phase 4.5) — finds keyword orphans (queries ranking 4-20 with no dedicated page), builds topic clusters from GSC data with pillar page recommendations, and identifies business-relevant keywords the site should rank for but has no impressions for.seo-analysis— Deep Metadata Audit — for each audited page, fetches the live<title>and<meta description>, cross-references against top GSC queries for title/query alignment, checks character counts, detects duplicate titles, and audits Open Graph tags. Outputs a structured per-page table.seo-analysis— Deep Schema Markup Audit — detects site type (E-commerce, SaaS, Local Business, etc.), defines expected schema types per site type, audits each page's<script type="application/ld+json">blocks, and flags missing high-impact schema and errors in existing schema. Cross-references with URL Inspection rich result findings.seo-analysis— Skill Handoffs (Phase 7) — after delivering the report, surfaces targeted follow-up actions:/meta-tags-optimizerfor pages with metadata issues,/schema-markup-generatorfor schema gaps,/keyword-researchwith seed terms from the gap analysis.- Branded vs non-branded segmentation (
branded_split) — pass--brand-terms "Acme,AcmeCorp"to split all GSC traffic into branded and non-branded segments. Each segment gets its own clicks, impressions, CTR, average position, query count, and top-20 queries. Non-branded metrics become the true baseline for Quick Wins and content recommendations. Returnsnullif no brand terms provided. - Page group clustering (
page_groups) — automatically buckets top pages by URL path pattern (/blog/, /products/, /locations/, /services/, /pricing/, /docs/, /about/, /faq/, /lp/, /case-studies/) with per-section aggregate stats. Exposes template-level problems: "all /products/ pages have 0.8% CTR" can be fixed once, not 50 times. - Winner/loser scoring for cannibalization — each
cannibalizationentry now includeswinner_page,winner_reason,loser_pages, andrecommended_action("consolidate: 301 redirect..." or "monitor: possible SERP domination"). test/unit/test_url_inspection.py— 25 unit tests coveringnormalize_site_url_for_inspection,parse_inspection_result, andsummarize_findings.- 35 new unit tests covering
classify_branded,derive_branded_split,cluster_page_groups, and all new cannibalization fields. - Strapi CMS integration (Phase 3.6) — the
/seo-analysisskill now cross-references your published Strapi content against GSC data. Three new scripts:preflight_strapi.py— validates config, tests connectivity, detects Strapi v4 vs v5. Exit code 2 = not configured (non-fatal skip).fetch_strapi_content.py— paginates all published entries, extracts SEO fields from the officialstrapi-community/plugin-seocomponent and root-level fallbacks, writes a structured JSON audit.push_strapi_seo.py— batch write-back with before/after diff preview, stale-write guard, and locale support for v5 localized content.
- 59 new unit tests for the Strapi scripts — version detection, entry normalisation, SEO audit counting, payload building, stale-write guard logic, and SSRF IP classification.
seo-analysis—analyze_gsc.pyparallelized — all 9 GSC API calls now run concurrently viaThreadPoolExecutor, cutting wall-clock data collection time by ~70%. Each worker has an exception guard so a single failed call logs an error and continues rather than crashing the script.url_inspection.py— parallel URL inspection — inspections run with--concurrency 3(default).--max-urlsdefault reduced from 20 to 5 to stay well within the 2000/day API quota. Worker failures are caught and logged without aborting the run.seo-analysis— technical crawl capped at 5 pages — Phase 5 now has a hard cap of 5 pages (homepage first, then top by clicks, then flagged pages) to keep the audit fast without losing insight.seo-analysis— broader OAuth scope — re-auth instructions throughout the skill now include bothwebmastersandwebmasters.readonlyscopes, required for the URL Inspection API.seo-analysisPhase 2 now asks for brand terms before pulling data.seo-analysisPhase 4 adds "Branded vs Non-Branded Split" and "Page Group Performance" sections.seo-analysis/evals/evals.json— 3-scenario test suite covering URL-first behavior, no-GSC technical fallback, and comprehensive GSC+inspection audit.- Cannibalization
competing_pagesnow sorted by position ascending (best first) instead of clicks descending. - Strapi integration is opt-in and non-blocking — if
STRAPI_URLis not configured, Phase 3.6 skips silently.
test/install.test.sh— mock-HOME install test suite for./setup. 61 assertions across 6 scenarios: Claude Code global install (symlinks, targets, preamble injection), auto-detect via path, idempotency, real-directory protection, Codex install (openai.yaml + SKILL.md symlinks), and invalid--hostflag handling. Includes a count-guard that fails fast if a new skill is added to the repo without updating the test's SKILLS array.
seo-analysis— deeper Google Search Console data in every audit. The script now pulls four additional data sets from a single API session:- Cannibalization (
cannibalization) — queries where multiple pages compete, with per-page click/impression breakdown. Previously the skill inferred this from single-dimension data; now it uses the real[query, page]dimension so every recommendation names specific URLs. - CTR gaps by page (
ctr_gaps_by_page) — high-impression, low-CTR pairs at the query+page level. Replaces query-only CTR opportunities so every title/meta rewrite suggestion includes the exact page to fix. - Country split (
country_split) — top 20 countries by clicks with CTR and position. Surfaces geo opportunities and region-specific ranking problems. - Search type breakdown (
search_type_split) — web, image, video, news, Discover, and Google News traffic shown separately. Many sites have Discover or image traffic they don't know about.
- Cannibalization (
device_splitnow includes CTR and position alongside clicks and impressions.- Phase 4 analysis guidance updated to use the new data fields directly.
- New "Segment Analysis" subsection added to Phase 4 for device, country, and search type interpretation.
- Unit tests: 49 → 79 (+30 tests covering all new functions with boundary and edge case coverage).
keyword-research— new skill for keyword discovery, intent classification, difficulty assessment, opportunity scoring, and topic clustering. Includes reference materials for intent taxonomy, prioritization framework, cluster templates, and example reports.meta-tags-optimizer— new skill for creating and optimizing title tags, meta descriptions, Open Graph, and Twitter Card tags with A/B test variations and CTR analysis. Includes reference materials for tag formulas, CTR benchmarks, and code templates.schema-markup-generator— new skill for generating JSON-LD structured data (FAQ, HowTo, Article, Product, LocalBusiness, etc.) with validation guidance and rich result eligibility checks. Includes reference materials for schema templates, decision tree, and validation guide.geo-content-optimizer— new skill for optimizing content to appear in AI-generated responses (ChatGPT, Perplexity, Google AI Overviews, Claude). Scores GEO readiness and applies citation, authority, and structure optimization techniques. Includes reference materials for AI citation patterns, GEO techniques, and quotable content examples.
- README.md — updated with documentation for all 4 new skills, expanded install instructions and directory tree
- Predictable /tmp paths —
analyze_gsc.pyandlist_gsc_sites.pynow usegsc_analysis_{uid}.json/gsc_sites_{uid}.jsonviatempfile.gettempdir()+os.getuid(), preventing cross-user data exposure on multi-user systems .gstack/gitignored — local security audit reports excluded from git history- Test dependency lockfile — added
requirements-test.lock(pip-compiled) to pin test dependencies and prevent supply-chain drift
preflight.py— pre-flight check that runs before any GSC operations; detects gcloud with OS-specific install instructions (Homebrew / apt / dnf / curl / winget), auto-triggersgcloud authbrowser flow if no ADC credentials foundsetup.py— cross-platform Python equivalent of./setupfor Windows users who can't run bash; falls back to directory junctions (no admin rights required) when symlinks are unavailable- Phase 0 in SKILL.md — preflight step added before GSC access check; also restores the "skip GSC → Phase 5" escape hatch for technical-only audits
seo-analysis/SKILL.md— Phase 1 simplified (error cases now handled by preflight); Phase 1 bash block is self-contained (no shell variable leak from Phase 0)
- README demo section — "See It Work" example conversation showing end-to-end
/seo-analysisflow for clearer onboarding
- Auto-upgrade on every skill use — removed the 4-option prompt (Yes / Always / Not now / Never); updates now apply automatically whenever
UPGRADE_AVAILABLEis detected - Update check frequency — reduced UP_TO_DATE cache TTL from 60 min to 5 min so checks run on nearly every skill invocation
- Zero-dependency GSC auth — removed
google-authPython package requirement; reverts 0.4.1 approach; scripts now callgcloud auth application-default print-access-tokendirectly via subprocess and use stdliburllibfor HTTP, eliminating thepip installsetup step gsc_auth.pyremoved — auth logic inlined inlist_gsc_sites.pyandanalyze_gsc.py; simpler, no shared module- SKILL.md Phase 1 — GSC setup instructions updated to reflect the simpler auth flow
- Predictable /tmp paths — GSC output files now use
gsc_analysis_{uid}.jsonandgsc_sites_{uid}.jsoninstead of shared paths, preventing cross-user data exposure on multi-user systems .gstack/gitignored — security audit reports are now excluded from git commits- Test dependency lockfile — added
requirements-test.lock(pip-compiled) to pin exact versions and prevent supply-chain drift
- GSC quota project header — replaced raw
urllibHTTP calls withgoogle-authlibrary (AuthorizedSession), which automatically sends thex-goog-user-projectheader required for ADC user credentials; this was the root cause of 403 errors during onboarding - Auto-detect quota project — scripts now read
quota_project_idfrom ADC credentials and fall back togcloud config get-value projectif missing, eliminating the manualset-quota-projectstep
- Shared auth module — extracted
gsc_auth.pywithget_credentials(),get_session(), and_ensure_quota_project()to eliminate duplicated auth logic betweenlist_gsc_sites.pyandanalyze_gsc.py - SKILL.md Phase 1 — streamlined GSC setup instructions from ~50 lines to ~25 lines for faster onboarding and lower token usage
- gsc_setup.md — updated setup guide to reflect 2-step process (
pip install google-auth+gcloud auth application-default login) and documented new troubleshooting entries
google-authdependency — new pip requirement for proper Google API authentication- 4 new unit tests for
_ensure_quota_project()covering: already-set, auto-detect from gcloud, gcloud not found, gcloud returns empty
content-writerskill — standalone SEO content creation, directly invocable without running a full SEO audit- Handles three jobs: new blog posts, new landing pages, and improving existing pages
- 6-step workflow: determine job → gather context → read guidelines → research & plan → write → quality gate
- Follows Google's E-E-A-T and Helpful Content guidelines via shared reference doc
- Outputs publication-ready content with SEO metadata, JSON-LD structured data, internal linking plan, and publishing checklist
- Smart content type detection from user intent (informational → blog, transactional → landing page)
content-writing.mdreference doc — single source of truth for Google content best practices (E-E-A-T framework, helpful content signals, blog/landing page templates, search intent matching, on-page SEO checklist, anti-patterns including AI content pitfalls)seo-analysisPhase 7 — optional content generation after audit; spawns up to 5 content agents in parallel when content gaps are identified, each reading the sharedcontent-writing.mdguidelines
- CONTRIBUTING.md — expanded with detailed SKILL.md structure, script requirements, reference file guidelines, and skill ideas table
- README.md — added
content-writerto skills table and updated project description
- Python test suite — full pytest infrastructure under
test/replacing the prior TypeScript/Bun approach; no build step requiredtest/unit/— 42 fast unit tests (stdlib only, no API calls); covers date math, GSC data processing, report structure, and skill SKILL.md content validationtest/test_skill_e2e.py— E2E skill tests gated behindEVALS=1; uses mockgcloud+ mockanalyze_gsc.pyfixture to run the full skill workflow without real credentialstest/test_skill_llm_eval.py— LLM-as-judge quality evals gated behindEVALS=1; scores report clarity, actionability, and phase coverage on a 1–5 scaletest/test_skill_routing_e2e.py— routing evals verify the skill triggers on SEO prompts and stays silent on unrelated requeststest/helpers/— session runner (spawnsclaude -p --output-format stream-json), LLM judge, eval store, and diff-based test selectiontest/fixtures/— mock gcloud binary, mock analyze_gsc.py, and sample GSC JSON fixture dataconftest.py— root-level pytest config for import path setuprequirements-test.txt— minimal test dependencies
- Routing tests — added harness failure guard;
should-not-triggertests no longer silently pass when the subprocess times out or crashes - Env isolation — test subprocess now strips
ANTHROPIC_*vars (in addition toCLAUDE_*) to preventANTHROPIC_BASE_URLorANTHROPIC_MODELfrom redirecting evals to an unintended endpoint - LLM judge retry — exponential backoff (3 attempts: 1s, 2s, 4s) replaces single-retry on rate limit
- Mock gcloud — removed fall-through to real
gcloudbinary that caused infinite recursion when mock was first in PATH .gitignore— restored credential patterns (credentials.json,token.json,.env, etc.) accidentally dropped in initial commit
- Simplified CONTRIBUTING.md — removed skill ideas table and verbose guidelines, kept essentials for getting started
- Rewrote README intro for clarity and power — headline now communicates that Toprank analyzes, recommends, and fixes SEO issues directly in your repo
- Autoupdate system — skills now check GitHub for new versions on every invocation
bin/toprank-update-check— fetchesVERSIONfrom GitHub with 60-min cache; outputsUPGRADE_AVAILABLE <old> <new>or nothingbin/toprank-config— read/write~/.toprank/config.yaml; supportsupdate_check,auto_upgradekeystoprank-upgrade/SKILL.md— upgrade skill with inline and standalone flows, snooze (24h/48h/7d backoff), auto-upgrade mode, changelog diff- Preamble in
seo-analysisand auto-inject viasetupfor all future skills bin/preamble.md— single source of truth for the preamble template
VERSIONfile — tracks current release for update checks
toprank-update-check: validate local VERSION format before writing cache; exit afterJUST_UPGRADEDto prevent dual stdout output; movemkdir -pto top of scriptsetup: atomic SKILL.md writes via temp file +os.replace(); addpipefailto catch silent Python errorstoprank-upgrade: clear stale.bakbefore vendored upgrade to prevent collision
seo-analysisPhase 1 — replaced two-step auth check (token print + separate site list) with singlelist_gsc_sites.pycall that tests auth, scopes, and GSC access in one shot; added distinct handling for each failure mode (wrong account, wrong scopes, API not enabled, gcloud not installed)seo-analysisscript paths — replaced hardcoded~/.claude/skills/seo-analysis/scripts/with afind-basedSKILL_SCRIPTSlookup that works for Claude Code, Codex, and custom install paths; added guard for empty result so missing installs fail with a clear error instead of a confusing path errorseo-analysisproperty selection — added explicit rule to prefer domain property (sc-domain:example.com) over URL-prefix when both exist for the same sitegsc_setup.md— moved "Which Google Account" guidance to top (most common failure cause); replaced brokenoauth_setup.pyOption B with Linux (Debian/Ubuntu, RPM) and Windows install instructions; fixed deprecatedapt-keywithgpg --dearmorfor Debian 12+/Ubuntu 24.04+; expanded troubleshooting to coverinsufficient_scope403s
list_gsc_sites.py— unhandledFileNotFoundErrorwhen gcloud is not installed now shows a clean error message; addedURLErrorhandling for network failures (DNS, TLS, proxy)analyze_gsc.py— sameFileNotFoundErrorandURLErrorfixesgsc_setup.md— removed reference tooauth_setup.pywhich did not existseo-analysisSKILL.md — corrected error-branch description from "Python traceback" to "ERROR: gcloud not found" to match the actual script output
- README intro — rewritten to lead with user outcome ("Finally know what to do about your SEO") and emphasize zero-risk install; blockquote examples now show real questions users would type
seo-analysisskill — comprehensive SEO audit powered by Google Search Console- Phase 1: GSC API setup detection and guided auth via
gcloudApplication Default Credentials - Phase 2: Auto-detect site URL from website repo (
package.json,next.config.js,astro.config.*, etc.) or prompt for URL - Phase 3: Data collection — top queries, top pages, position buckets (1–3, 4–10, 11–20, 21+), CTR opportunities, 28-day period comparison, device split
- Phase 4a: Search Console analysis — quick wins, content gaps, traffic drops
- Phase 4b: Technical SEO audit — indexability, meta tags, heading structure, structured data, performance signals
- Phase 5: Structured report with executive summary, traffic snapshot, and 30-day action plan
- Phase 1: GSC API setup detection and guided auth via
scripts/list_gsc_sites.py— list all GSC properties for the authenticated accountscripts/analyze_gsc.py— pull and process GSC data, output structured JSONreferences/gsc_setup.md— complete setup guide for gcloud ADC and OAuth fallback