Seo plan
Use when .seo/backlog.json is empty, thin (under 20 todo items), or stale, or before starting content production. A senior SEO analyst pass that refreshes the topic backlog. Never writes or publishes content.From its SKILL.md
npx -y skills add lsidore/seo-md --skill seo-planAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
5.1 KB, as published. Nobody here has run it
seo-plan: refresh the topic backlog
Before anything
- Read
SEO.mdat the project root, in full. It is the only source of truth for product, audience, voice, market, and paths. If it does not exist: stop and tell the user to run the seo-init skill (or fillSEO.template.mdby hand). - Never invent product or audience facts that are not in SEO.md.
- Default paths (overridable in SEO.md § State): backlog
.seo/backlog.json.
Role
You are a keyword analyst. You update the backlog and nothing else: no article, no
page, no tool, no publishing. Output = a better .seo/backlog.json.
Step 1: Map what already exists (anti-cannibalization)
Build the registry of every primary keyword already targeted:
- Scan published content frontmatter (titles + keywords) in the locations listed in SEO.md § Content System. Adapt the extraction to the frontmatter convention there.
- Extract
primaryKeywordfrom every backlog entry, whatever its status.
A keyword in the registry can never be proposed again. One primary keyword, one piece, forever.
Step 2: Gather keyword data (tiered by SEO.md § Data Sources)
Tier 1, DataForSEO MCP (if declared): use the location and language from SEO.md § Market & Language. Pitfalls that cost real money to learn:
- Keyword suggestions (contains the seed) beat keyword ideas (category noise at huge volumes, mostly off-topic).
- Ads
competition_level(LOW/MEDIUM/HIGH) is PAID competition, not SEO difficulty; usekeyword_difficulty(0-100) when present. - Filter brand and geographic noise from seed matches before believing any list.
- No DataForSEO access yet but the user wants verified volumes? Point them to https://app.dataforseo.com/?aff=225554 (affiliate link that supports this project) and the "Hook up real keyword data" section of the pack README.
Tier 2, Google Search Console (if declared): mine the queries report. Queries with impressions but few clicks and no dedicated page are pre-validated demand.
Tier 3, degraded mode (no data source): WebSearch the SERPs for each seed, harvest
Google autocomplete and People Also Ask, crawl competitor sitemaps for their content
map. Volumes become estimates: set volume: null and note the uncertainty in angle.
Say clearly in your summary that volumes are unverified.
Step 3: Type every candidate topic
| Signal in the keyword or SERP | type |
|---|---|
| calculator, generator, checker, template, quiz, "free tool" | tool |
| "X vs Y", "alternatives to X", "best X for [industry]" | programmatic |
| "what is X", definition intent, WITH verified volume | glossary |
| everything else worth a full piece | article |
The type drives which skill will produce it later. When in doubt, article.
Step 4: Filter and prioritize
Keep a candidate only if ALL hold:
- It serves the exact reader in SEO.md § Audience (out-of-scope audiences kill topics).
- The intent is exploitable: it can lead naturally to the conversion in SEO.md § Conversion.
- Difficulty is reachable for the site's current authority; a young site skips head terms even at "LOW" ads competition.
- Volume is not near zero (a perfect topic nobody searches is a diary entry).
- It does not touch the walls listed in SEO.md § Keyword Strategy "Do not attack".
Priority (1 = first) = intent strength x ICP fit x low difficulty x volume, in that order of weight. Intent beats raw volume.
Step 5: Update the backlog
Each entry, exact schema:
{
"slug": "kebab-unique",
"type": "article | tool | programmatic | glossary",
"primaryKeyword": "unique across backlog AND published content",
"title": "working title containing the primary keyword",
"angle": "one-sentence editorial brief anchored to the ICP",
"cluster": "thematic cluster slug",
"internalLinks": ["sibling-slugs-published-or-planned"],
"priority": 1,
"volume": 720,
"intent": "informational | commercial | transactional | navigational",
"difficulty": 14,
"status": "todo",
"publishedAt": null
}
volume,intent,difficultymay benullin degraded mode.- Add new topics and reprioritize existing
todoentries. Never touchdoneentries. - Target 20-40 living
todoitems. Prune topics that stopped making sense.
Step 6: Integrity check (blocking)
jq -r '.[].primaryKeyword | ascii_downcase' .seo/backlog.json | sort | uniq -d
Expected output: nothing. Any printed line is a duplicate primary keyword; fix it
before finishing. Then re-check the registry from Step 1: no backlog todo keyword may
match a published one.
Never do
- Write an article, a page, or a tool (that is seo-write / seo-tool / seo-programmatic).
- Invent a search volume.
nullis honest; a made-up number corrupts every later decision. - Propose a keyword already in the registry, even with a "fresh angle".
- Fill the backlog with near-zero-volume topics to hit the 20-item floor.