A Claude Code skill that audits and grows SEO for small business websites on zero budget. Two layers: (1) a 10-step technical audit — reads HTML directly, computes contrast ratios, checks sitemaps, validates schema, outputs a prioritised findings report; and (2) a growth loop — reads first-party analytics browser-free, tracks the organic-vs-social traffic mix week over week, sets up UTM short links + campaign attribution, and keeps a living progress log so each run progresses instead of regressing.
This repository contains a single Claude Code skill. The canonical file is SKILL.md. Everything below describes how to install, invoke, and extend it.
claude-seo-skill/
├── SKILL.md ← The skill. Install this file. Do not modify for per-project use.
├── CLAUDE.md ← Instructions for agents working inside this repo (contributors).
└── README.md ← This file.
~/.claude/skills/seo/SKILL.md
This is the global Claude Code skills directory. Skills installed here are available in every project via /seo.
/seo
Or with arguments:
/seo run audit on index.html and services.html
/seo audit all pages
/seo check contrast on homepage
| Capability | Input | Output |
|---|---|---|
| Full 10-step audit | HTML file path(s) or glob | Prioritised findings report (Critical / High / Medium / Low) |
| Single-step audit | Step name + HTML path | Focused findings for that step only |
| Direct fix | Authorisation phrase + findings | Edited HTML files with stated changes |
| Sitemap generation | List of public page URLs | Valid sitemap.xml |
| Schema injection | Page type + business details | <script type="application/ld+json"> block |
| Contrast check | CSS colour values (hex/rgba + opacity) | Pass/fail ratio with fix suggestion |
| Traffic read | Keyed analytics endpoint (browser-free) | Visits, source split, bot-filtered click data |
| Weekly mix report | daily + daily_sources |
Rolling 7-day organic-vs-social comparison |
| Short-link + UTM setup | Destinations + platform list | /go/<slug> redirector, links.json, public manifest |
| Campaign attribution | Launch + landing URL | campaigns.json row; result filled vs baseline |
| Living state | Prior PROGRESS.md + live check |
Dated progress entry; no regression between runs |
1 Crawlability — sitemap.xml, robots.txt, page listed, Googlebot not blocked
2 Title tag — 50–60 chars, keyword-first, brand at end
3 Meta desc — 140–155 chars, intent match, soft CTA
4 Headings — exactly one H1 per page, keyword present, no skipped levels
5 Schema — Organization / Service / LocalBusiness / Person; no fabricated fields
6 Internal links — orphaned pages, descriptive anchor text
7 Images — alt text on content images, loading="lazy" below fold
8 Contrast — WCAG AA formula + opacity blending; thresholds 4.5:1 / 3:1
9 Page speed — render-blocking scripts, missing image dimensions, font preconnect
10 Intent match — primary keyword stated; content answers the query
A labelled section in SKILL.md turns inside-out page titles into ones that target phrases people actually search — using web search, no paid keyword tools:
Research — 1-3 web searches per service in the customer's words; read the dominant
vocabulary, judge head-vs-long-tail, and reject intent mismatches
Map — one primary phrase per page (each used by only one page)
Titles — rewrite <title> only: 50-60 chars, phrase near front, brand at end, distinct
Re-measure — set baseline_pos null now; pull GSC positions on a later run
Titles-only is deliberate: highest-ROI, zero layout risk. Meta/body come later.
Beyond the per-page audit, four labelled sections in SKILL.md run the compounding loop:
Measurement — read first-party analytics browser-free (UA + cookie-jar past bot
challenges); bot-filter always; track organic-search share and the
weekly organic-vs-social mix; snapshot baseline.json + weekly-baseline.json
Attribution — UTM taxonomy (stable campaign, per-platform source/medium); a /go/<slug>
302 shortener with UTMs baked in + first-party click log; campaigns.json
Living state — PROGRESS.md is the authoritative running log; read first, live-check,
never repeat a stale flag; append a dated entry every run
Subdomains — shared analytics.js per property; www→apex 301; per-subdomain tags + sitemap
Every audit produces output in this exact structure:
## SEO Audit: [page name] ([file path])
**Primary keyword target**: [phrase]
### Critical
- [finding] — [why] — Fix: [action]
### High
### Medium
### Low / Nice to have
### Already good
Sections with zero findings are omitted.
- No keyword stuffing.
- No fabricated schema fields (awards, ratings, employee counts, accreditations).
- No invented E-E-A-T signals (qualifications, years of experience, client counts).
- Title and meta edits only — no body copy rewrites without explicit approval.
- State each change and its reason before editing.
- Do not alter HTML structure, class names, or JS when fixing meta/schema.
Contributions are improvements to SKILL.md. A valid contribution is one of:
- A new audit check with: name, how to detect from source, pass/fail criteria, output format, fix template
- An improvement to an existing check: more precise detection logic, better fix template, reduced false positives
- A new schema template: must use only verifiable fields, no fabricated data
- A platform-specific appendix: WordPress, Squarespace, Framer, etc.
See CLAUDE.md for the contribution workflow an agent should follow when working inside this repo.
Built for solvX.uk as part of a public SEO experiment documented at solvx.uk/seo-lab.html. Generalised here for any small business site.
MIT licence.