seo-measure
Skill0 starsChanged 7 months agoMITView on GitHub
Weekly SEO + GEO measurement and learning loop. Pulls Google Search Console data, runs live AI citation checks (ChatGPT, Claude, Perplexity), compares against last week's baseline, and writes findings to per-site memory so the system compounds site-specific expertise over time. Use when the user says "measure seo", "seo weekly", "did it work", "check rankings", "am i getting cited by ai", "track seo progress", "weekly seo report", or after running seo-audit to capture the first baseline.
The file
Show its rules and text
We split this skill into the rules it gives an agent
16 rules. About 0 are common, 10 are distinctive, and 6 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈10
Distinctive · estimate
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
After seo-audit completes — capture the pre-fix baseline immediately
- Distinctive: line 16
After shipping an SEO/GEO change — measure impact at +7 days, +14 days, +28 days
- Distinctive: line 17
When the user asks "did it work", "is the change showing up", "am i getting more AI citations"
- Distinctive: line 33
All of these should be stored in the site memory file on first run and reused automatically on subsequent runs.
- Distinctive: line 37
Run these in parallel on the main thread (they're independent).
- Very specific: line 53
Ask the user to export from Search Console → Performance → last 7 days → Export → CSV, then paste the path.
- Distinctive: line 93
If user can't run the prompts manually, run what you can automatically (Perplexity) and flag the rest as "user action required — report …
- Distinctive: line 114
If logs aren't accessible, skip this phase and note "server logs not available — add Vercel log forwarding or server log access to unlock …
- Distinctive: line 132
Read the per-site memory file (see Phase 3 for path).
- Distinctive: line 274
Never fabricate metrics: If GSC wasn't accessible, say "GSC not pulled this week" — don't invent numbers.
- Very specific: line 276
Strong signals get promoted: When a signal crosses strong after 3+ weeks of confirmation, suggest adding it as a site-specific note in …
- Distinctive: line 284
If the site has no memory directory yet:
- Very specific: line 286
Create ~/.claude/projects/memory-seo/{{site_slug}}/ with empty history/, learnings.md, regressions.md
- Distinctive: line 288
Run Phase 1 (pull what you can)
- Very specific: line 291
Write baseline.json with Phase 1 output
- Very specific: line 296
After seo-audit finishes its report, it should offer: *"Capture this as a baseline in seo-measure?
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
SEO Measurement & Learning Loop
The compounding engine. An audit is a snapshot; this is the learning system. Run weekly (or after every meaningful SEO change) so the team accumulates site-specific empirical knowledge no generic expert could match.
The theoretical gap between a well-researched checklist and a 20-year SEO expert closes through one thing: measurement density on a specific site across many iterations. That's what this skill exists for.
When to Use
- After
seo-auditcompletes — capture the pre-fix baseline immediately - Weekly cadence (via
/loop 7d seo-measureor/schedule) — track progress, diff vs last week - After shipping an SEO/GEO change — measure impact at +7 days, +14 days, +28 days
- When the user asks "did it work", "is the change showing up", "am i getting more AI citations"
Preflight
Before running, establish:
- Site identifier. A canonical slug (e.g.,
example.com). Used for the memory file path. Ask if unclear. - Live URL. Needed for AI citation + crawler simulation.
- GSC access mode. Three options — ask which:
- (a) GSC API via service account — automatic, requires setup once. Recommended long-term.
- (b) Manual CSV export — user pastes or uploads GSC "Performance" export. Works today, zero setup.
- (c) Skip GSC this run — use only AI citation checks + crawler sim + server logs. Fine for week 1.
- Server log access. For crawl-budget measurement. Optional but high-value. Path to access logs if available (Vercel logs, Nginx, Cloudflare Analytics).
- Target keywords. Up to 10 the user cares about ranking for. Used for rank-tracking deltas.
- Target entities. Up to 5 "who/what" prompts for AI citation checks (site name, author name, org name, a key product, a key service). Used in Phase 2.
All of these should be stored in the site memory file on first run and reused automatically on subsequent runs.
Phase 1 — Pull the data
Run these in parallel on the main thread (they're independent).
1a. Google Search Console
Option A (API):
# Requires google-auth-httplib2 + google-api-python-client, or gsc-cli
gsc query "$SITE_URL" \
--start-date "$(date -v-7d +%Y-%m-%d)" \
--end-date "$(date +%Y-%m-%d)" \
--dimensions query,page \
--row-limit 1000 \
--format csv > /tmp/gsc-$(date +%Y%m%d).csvOption B (manual CSV):
Ask the user to export from Search Console → Performance → last 7 days → Export → CSV, then paste the path. Parse with awk/jq/pandas-equivalent logic.
Option C (skip): proceed to 1b.
Extract:
- Total clicks, impressions, CTR, average position — last 7 days
- Top 20 queries by clicks
- Top 20 pages by clicks
- New queries (appearing this week, not last week)
- Lost queries (in last week's data, not this week's)
- Queries with position change ≥ 3 (either direction)
- Coverage status (indexed / excluded / errors) — from the "Pages" report if available
1b. AI Citation Check (the GEO metric)
This is the only metric that directly proves AI-search visibility. For each target entity from preflight, query multiple AI search engines and record whether the site is cited.
Prompts to run (via WebFetch on Perplexity-style interfaces, or instruct the user to paste into ChatGPT/Claude):
For each target_entity in [site_name, author_name, org_name, product, service]:
Prompt 1: "What is {{target_entity}}?"
Prompt 2: "Who is {{target_entity}}?" (if person/org)
Prompt 3: "{{target_entity}} services" or "{{target_entity}} features"
Prompt 4: "Best {{category}} for {{use case}}" — where user expects to be citedFor each (entity, prompt, engine) tuple, record:
- Cited? (yes/no)
- Position in answer (early / middle / late)
- Citation URL (which page)
- Context (what the AI said about you)
Engines to check:
- Perplexity (easiest — has public API via WebFetch-friendly URLs)
- ChatGPT Search (requires user to paste — assistant can't browse as logged-in user)
- Claude (same — ask user to paste the answer)
- Google AI Overviews (check if SERP shows an AI Overview for the query)
If user can't run the prompts manually, run what you can automatically (Perplexity) and flag the rest as "user action required — report back next run."
1c. Server Log Analysis (if logs available)
LOG_PATH="{{user-provided}}"
SINCE=$(date -v-7d +%Y-%m-%dT%H)
# AI crawler hit frequency
for BOT in GPTBot OAI-SearchBot ChatGPT-User ClaudeBot anthropic-ai PerplexityBot Perplexity-User Google-Extended Applebot-Extended CCBot Googlebot Bingbot; do
count=$(grep "$BOT" "$LOG_PATH" | awk -v s="$SINCE" '$1 >= s' | wc -l)
echo "$BOT: $count hits"
done
# Most-crawled URLs by Googlebot (crawl budget signal)
grep "Googlebot" "$LOG_PATH" | awk '{print $7}' | sort | uniq -c | sort -rn | head -20
# 404s from any bot
grep -E "Googlebot|GPTBot|ClaudeBot|PerplexityBot" "$LOG_PATH" | grep ' 404 ' | awk '{print $7}' | sort | uniq -c | sort -rn | head -20If logs aren't accessible, skip this phase and note "server logs not available — add Vercel log forwarding or server log access to unlock crawl-budget measurement."
1d. Live crawler re-simulation
Re-run the crawl simulation block from seo-audit Phase 5a. Compare raw HTML content-length, h1 count, JSON-LD count against last run. Any regression = 🐛.
1e. Lighthouse field data (CrUX)
# PageSpeed Insights API — field data (real users), not lab
curl -s "https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=$URL&strategy=mobile&category=performance&category=seo&category=accessibility" \
| jq '.loadingExperience.metrics'Extract field LCP, INP, CLS percentiles. Lab Lighthouse can pass while field fails — this is the ground truth.
Phase 2 — Diff against last week
Read the per-site memory file (see Phase 3 for path). If it doesn't exist, this is the baseline run — record but don't diff.
Compute:
| Metric | Last week | This week | Δ | Verdict |
| Total clicks | 412 | 487 | +18% | ⬆ |
| Total impressions | 8,221 | 9,104 | +11% | ⬆ |
| Avg position | 23.4 | 19.8 | −3.6 | ⬆ |
| Indexed pages | 47 | 49 | +2 | ⬆ |
| Pages with Rich Results | 12 | 18 | +6 | ⬆ |
| AI citations (Perplexity) | 2/5 | 4/5 | +2 | ⬆ |
| AI citations (ChatGPT) | 1/5 | 2/5 | +1 | ⬆ |
| GPTBot hits (7d) | 0 | 34 | +34 | ⬆ |
| ClaudeBot hits (7d) | 0 | 12 | +12 | ⬆ |
| Field LCP p75 | 2,840ms | 2,410ms | −430ms | ⬆ |
| Field INP p75 | 280ms | 190ms | −90ms | ⬆ |
Flag any regression as 🐛. Flag any improvement as a win worth attributing.
Phase 3 — Attribution (the learning step)
For every metric movement ≥ 10% (in either direction), try to explain it. Read the git log for commits to this repo in the past 7 days:
git -C {{repo-path}} log --since="7 days ago" --oneline --name-onlyCross-reference SEO-touching commits against metric changes. Examples:
- "Added BlogPosting schema to /blog/X on Mar 5 → +214% impressions on that URL = schema working"
- "Removed llms.txt Mar 3 → GPTBot hits dropped 80% = llms.txt was being crawled after all, put it back"
- "Shipped per-route metadata Feb 28 → avg position on 12 pages improved by 4+ = inherited-title fix delivered"
- "CLS regression from new hero image → LCP p75 worsened 600ms = needs priority hint + dimensions"
Attribution is inherently fuzzy. Be honest: mark each as (strong / plausible / speculative). Over time the strong signals compound.
Phase 4 — Write findings to site memory
Memory path convention:
~/.claude/projects/memory-seo/{{site-slug}}/
├── config.json # target keywords, entities, URL, GSC mode
├── baseline.json # first-run snapshot
├── history/
│ ├── 2026-04-12.json # each weekly run
│ ├── 2026-04-19.json
│ └── ...
├── learnings.md # accumulated attribution findings (strong signals only)
└── regressions.md # every 🐛 ever logged, with date + cause if knownEach weekly snapshot (history/YYYY-MM-DD.json):
{
"date": "2026-04-12",
"gsc": { "clicks": 487, "impressions": 9104, "avgPosition": 19.8, "topQueries": [...], "newQueries": [...], "lostQueries": [...] },
"ai_citations": { "perplexity": { "cited": 4, "total": 5, "details": [...] }, "chatgpt": {...}, "claude": {...} },
"crawler_hits": { "Googlebot": 1240, "GPTBot": 34, "ClaudeBot": 12, ... },
"field_vitals": { "lcp_p75": 2410, "inp_p75": 190, "cls_p75": 0.08 },
"commits_this_week": [ { "sha": "...", "message": "...", "seo_relevant": true } ]
}learnings.md — append-only knowledge base (the compounding artifact):
## {{date}}
### Strong signals
- **{{Schema type}} moves the needle on {{site_slug}}.** Added to {{route}} on {{deploy date}}. By {{measure date}}: impressions on that URL went from {{before}} → {{after}} ({{delta}}), clicks {{before}} → {{after}}. Google Rich Results Test confirmed eligibility {{n}} hours after deploy. **Rule:** prioritize {{schema type}} on this site's {{content type}} before other schema work.
- **{{AI crawler}} only started crawling after llms.txt landed.** {{N}} hits weeks 1-2, {{M}} hits week 3 (llms.txt went live {{date}}). Correlation is strong, causation uncertain. **Rule:** llms.txt is not free — it may drive AI crawler discovery on small sites even if not a ranking signal.
### Plausible signals
- Removing `images: { unoptimized: true }` correlated with −{{n}}ms LCP p75 improvement. Could also be the CDN change that shipped same day. Revisit next week.
### Regressions logged
- None this week.
## {{previous week date}}
...Strong signals from learnings.md should be promoted into the seo-audit skill as site-specific overrides. Cross-skill knowledge transfer is the whole point.
config.json — stable per-site setup:
{
"site_slug": "{{example.com}}",
"url": "https://{{example.com}}",
"repo_path": "{{/absolute/path/to/site-repo}}",
"gsc_mode": "{{api | manual_csv | skip}}",
"target_keywords": ["{{keyword 1}}", "{{keyword 2}}"],
"target_entities": ["{{brand or person name}}", "{{product name}}", "{{service name}}"],
"log_path": "{{/path/to/access.log or null}}",
"first_run": "{{YYYY-MM-DD}}"
}Phase 5 — Generate the weekly report
# Weekly SEO Report — {{site_slug}}
**Week of:** {{start}} to {{end}}
**Mode:** {{live / baseline}}
## Top-line movement
{{metric diff table}}
## New wins this week
- {{strong-attribution improvements}}
## Regressions (🐛)
- {{anything that got worse}}
## AI citations — GEO scoreboard
| Entity | Perplexity | ChatGPT | Claude |
| ... |
## Attributions (what worked, what didn't)
- Strong: ...
- Plausible: ...
- Speculative: ...
## New queries appearing in GSC
- {{list}}
## Lost queries
- {{list}}
## Recommendations for next week (ranked by expected ROI based on site learnings)
1. ...
2. ...
3. ...
## Memory updates
- Wrote history/{{date}}.json
- Appended {{n}} signals to learnings.md
- {{any config changes}}Rules
- Never fabricate metrics. If GSC wasn't accessible, say "GSC not pulled this week" — don't invent numbers.
- Attribution honesty is load-bearing. Mark every claim as strong/plausible/speculative. The long-term value of
learnings.mddepends on signal, not noise. - Strong signals get promoted. When a signal crosses strong after 3+ weeks of confirmation, suggest adding it as a site-specific note in
seo-audit's "Known Anti-Patterns" or "Reference Implementations" sections. - No changes to the site on a measure run. This skill reads and writes memory only. Fixes go through
seo-audit→ user approval. - Baseline run is valid output. First run produces no diff. The snapshot itself is the deliverable — compound starts next week.
- Run
seo-measureon a schedule, not on vibes. Recommend/loop 7d seo-measureor/schedule "0 9 * * MON"so weeks don't get skipped. Compounding requires density. - Honor the traffic goal. A GEO-first user's report leads with AI citations; a Google-first user's leads with GSC. Same skill, re-ordered presentation.
First-Run Bootstrap
If the site has no memory directory yet:
- Create
~/.claude/projects/memory-seo/{{site_slug}}/with emptyhistory/,learnings.md,regressions.md - Prompt for all preflight inputs and save to
config.json - Run Phase 1 (pull what you can)
- Skip Phase 2 (no last-week data)
- Skip Phase 3 (no commits to attribute yet)
- Write
baseline.jsonwith Phase 1 output - Tell user: "Baseline captured. Re-run in 7 days to see first diff. Set up
/loop 7d seo-measureor/scheduleto automate."
Integration with seo-audit
After seo-audit finishes its report, it should offer: "Capture this as a baseline in seo-measure? (recommended — starts the compounding loop)". If yes, invoke seo-measure with the same site + findings carried over.
Pre-audit check: if learnings.md exists for this site, seo-audit should read it first and let site-specific learnings override generic priorities. That's how the system gets smarter about YOUR sites over time — not generic SEO experts, but experts on this site.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

