Quick addendum: I'm aware there's a broader debate (see anthropics/claude-code#26179) that subagents shouldn't default to Opus/inherit since most focused, well-scoped subagent tasks do fine on Sonnet, and blanket-Opus wastes resources. I want to be clear that's not what I'm proposing here. This isn't "more Opus everywhere" — it's the opposite: keep Sonnet as the default for the 13 mechanical/extraction agents (backlinks, dataforseo, ecommerce, flow, google, image-gen, local, maps, performance, schema, sitemap, technical, visual), and only escalate the 5 agents whose job is genuinely open-ended… read the rest
Semantic Topic Clustering
SERP-based semantic topic clustering for content architecture planning.
How to use it
- Hit Copy SKILL.md — or use the Claude Code line below to get every file.
- Claude: ⋯ → Download .md, then Customize → Skills → Add → Upload skill.
ChatGPT: make a Project and paste it into Instructions.
Neither? Paste it at the top of a new chat — it works for that chat. - Describe your job in plain words. The AI follows the skill from there.
npx degit AgriciDaniel/claude-seo/skills/seo-cluster#main ~/.claude/skills/seo-clusterFor one project only, change the path to .claude/skills/seo-cluster. This skill also uses dataforseo_costs.py, cluster-plan.json, cluster-plan.md, cluster-scorecard.md, render_page.py — copying SKILL.md alone won't be enough. See the folder on GitHub.
Not working?
- Check which app you pasted it into — the steps above name the right one.
- Some skills need the paid tier of Claude or ChatGPT.
Paste into Claude, ChatGPT or Cursor.
Show the full text323 lines
Semantic Topic Clustering
SERP-overlap-driven keyword clustering for content architecture. Groups keywords by how Google actually ranks them (shared top-10 results), not by text similarity. Designs hub-and-spoke content clusters with internal link matrices and generates interactive cluster map visualizations.
Scripts: Located at the plugin root scripts/ directory.
Quick Reference
| Command | What it does |
|---|---|
/seo cluster plan <seed-keyword> |
Full planning workflow: expand, cluster, architect, visualize |
/seo cluster plan --from strategy |
Import from existing /seo plan output |
/seo cluster execute |
Execute plan: create content via claude-blog or output briefs |
/seo cluster map |
Regenerate the interactive cluster visualization |
Planning Workflow
Step 1: Seed Keyword Expansion
Expand the seed keyword into 30-50 variants using WebSearch:
- Related searches: Search the seed, extract "related searches" and "people also search for"
- People Also Ask (PAA): Extract all PAA questions from SERP results
- Long-tail modifiers: Append common modifiers: "best", "how to", "vs", "for beginners", "tools", "examples", "guide", "template", "mistakes", "checklist"
- Question mining: Generate who/what/when/where/why/how variants
- Intent modifiers: Add commercial modifiers: "pricing", "review", "alternative", "comparison", "free", "top"
Deduplication: Normalize variants (lowercase, strip articles), remove exact duplicates. Target: 30-50 unique keyword variants. If under 30, run a second expansion pass with the top PAA questions as seeds.
Step 2: SERP Overlap Clustering
This is the core differentiator. Load references/serp-overlap-methodology.md for
the full algorithm.
Process:
- Group keywords by initial intent guess (reduces pairwise comparisons)
- For each candidate pair within a group, WebSearch both keywords
- Count shared URLs in the top 10 organic results (ignore ads, featured snippets, PAA)
- Apply thresholds:
| Shared Results | Relationship | Action |
|---|---|---|
| 7-10 | Same post | Merge into single target page |
| 4-6 | Same cluster | Group under same spoke cluster |
| 2-3 | Interlink | Place in adjacent clusters, add cross-links |
| 0-1 | Separate | Assign to different clusters or exclude |
Optimization: With 40 keywords, full pairwise = 780 comparisons. Instead:
- Pre-group by intent (4 groups of ~10 = 4 x 45 = 180 comparisons)
- Only cross-check group boundary keywords
- Skip pairs where both are long-tail variants of the same head term (assume same cluster)
DataForSEO integration: If DataForSEO MCP is available, use serp_organic_live_advanced
instead of WebSearch for SERP data. Run "${CLAUDE_PLUGIN_ROOT}/scripts/claude-seo" run dataforseo_costs.py check serp_organic_live_advanced --count N
before each batch. If "status": "needs_approval", show cost estimate and ask user.
If "status": "blocked", fall back to WebSearch.
Step 3: Intent Classification
Classify each keyword into one of four intent categories:
| Intent | Signals | Include in Clusters? |
|---|---|---|
| Informational | how, what, why, guide, tutorial, learn | Yes |
| Commercial | best, top, review, comparison, vs, alternative | Yes |
| Transactional | buy, price, discount, coupon, order, sign up | Yes |
| Navigational | brand names, specific product names, login | No (exclude) |
Remove navigational keywords from clustering. Flag borderline cases for manual review. Keywords can have mixed intent (e.g., "best CRM software" is both commercial and informational) -- classify by dominant intent.
Step 4: Hub-and-Spoke Architecture
Load references/hub-spoke-architecture.md for full specifications.
Design the cluster structure:
- Select the pillar keyword: Highest volume, broadest intent, most SERP overlap with other keywords
- Group spokes into clusters: Each cluster is a subtopic area (2-5 clusters per pillar)
- Assign posts to clusters: Each cluster gets 2-4 spoke posts
- Select templates per post: Based on intent classification:
| Intent Pattern | Template Options |
|---|---|
| Informational (broad) | ultimate-guide |
| Informational (how) | how-to |
| Informational (list) | listicle |
| Informational (concept) | explainer |
| Commercial (compare) | comparison |
| Commercial (evaluate) | review |
| Commercial (rank) | best-of |
| Transactional | landing-page |
Set word count targets:
- Pillar page: 2500-4000 words
- Spoke posts: 1200-1800 words
Cannibalization check: No two posts share the same primary keyword. If SERP overlap is 7+, merge those keywords into a single post targeting both.
Step 5: Internal Link Matrix
Design the bidirectional linking structure:
| Link Type | Direction | Requirement |
|---|---|---|
| Spoke to pillar | spoke -> pillar | Mandatory (every spoke) |
| Pillar to spoke | pillar -> spoke | Mandatory (every spoke) |
| Spoke to spoke (within cluster) | spoke <-> spoke | 2-3 links per post |
| Cross-cluster | spoke -> spoke (other cluster) | 0-1 links per post |
Rules:
- Every post must have minimum 3 incoming internal links
- No orphan pages (every post reachable from pillar in 2 clicks)
- Anchor text must use target keyword or close variant (no "click here")
- Link placement: within body content, not just navigation/sidebar
Generate the link matrix as a JSON adjacency list:
{
"links": [
{ "from": "pillar", "to": "cluster-0-post-0", "type": "mandatory", "anchor": "keyword" },
{ "from": "cluster-0-post-0", "to": "pillar", "type": "mandatory", "anchor": "keyword" }
]
}
Step 6: Interactive Cluster Map
Generate cluster-map.html using the template at templates/cluster-map.html.
- Read the template file
- Build the
CLUSTER_DATAJSON object from the cluster plan:{ pillar: { title, keyword, volume, template, wordCount, url }, clusters: [{ name, color, posts: [{ title, keyword, volume, template, wordCount, url, status }] }], links: [{ from, to, type }], meta: { totalPosts, totalClusters, totalLinks, estimatedWords } } - Replace the
CLUSTER_DATAplaceholder in the template with the actual JSON - Write the completed HTML file to the output directory
- Inform user: "Open
cluster-map.htmlin a browser to explore the interactive cluster map."
Strategy Import
When invoked with --from strategy:
- Look for the most recent
/seo planoutput in the current directory (search for files matching*SEO*Plan*,*strategy*,*content-strategy*) - Parse markdown tables for: keywords, page types, content pillars, URL structures
- Validate extracted data: check for duplicates, missing keywords, incomplete entries
- Enrich with SERP data: run SERP overlap analysis on extracted keywords
- Build cluster plan using the imported keywords as the starting set (skip Step 1)
If no strategy file is found, prompt the user: "No existing SEO plan found in the
current directory. Run /seo plan first, or provide a seed keyword for fresh clustering."
Execution Workflow
When /seo cluster execute is invoked:
Check for claude-blog
Test: Does ~/.claude/skills/blog/SKILL.md exist?
If claude-blog IS installed:
- Load
references/execution-workflow.mdfor the full algorithm - Read
cluster-plan.jsonfrom the current directory - Check for resume state: scan output directory for already-written posts
- Execute in priority order: pillar first, then spokes by volume (highest first)
- For each post, invoke the
blog-writeskill with cluster context:- Cluster role (pillar or spoke)
- Position in cluster (cluster index, post index)
- Target keyword and secondary keywords
- Template type and word count target
- Internal links to include (with anchors)
- Links to receive from future posts (placeholder markers)
- After each post is written, scan previous posts for backward link placeholders and inject the new post's URL
- After all posts are written, generate the cluster scorecard
If claude-blog is NOT installed:
- Generate detailed content briefs for each post in the cluster plan
- Each brief includes:
- Title and meta description
- Primary keyword and secondary keywords
- Template type and suggested structure (H2/H3 outline)
- Word count target
- Internal links to include (with anchor text)
- Key points to cover
- Competing pages to differentiate from
- Write briefs to
cluster-briefs/directory as individual markdown files - Inform user: "Install claude-blog
to auto-create content. Briefs saved to
cluster-briefs/."
Cluster Scorecard
Post-execution quality report. Run automatically after /seo cluster execute or
on demand via analysis of the output directory.
| Metric | Target | How Measured |
|---|---|---|
| Coverage | 100% | Posts written / posts planned |
| Link Density | 3+ per post | Count internal links per post |
| Orphan Pages | 0 | Posts with < 1 incoming link |
| Cannibalization | 0 conflicts | Check for duplicate primary keywords |
| Image Count | 1+ per post | Posts with at least one image |
| Pillar Links | 100% | All spokes link to pillar and vice versa |
| Cross-Links | 80%+ | Recommended spoke-to-spoke links implemented |
| Content Gaps | 0 | Planned posts that were skipped or incomplete |
Map Regeneration
When /seo cluster map is invoked:
- Read
cluster-plan.jsonfrom the current directory - Scan output directory and update post statuses (planned vs written)
- Regenerate
cluster-map.htmlwith updated statuses - Report: posts written vs planned, link completion percentage
Output Files
All outputs are written to the current working directory:
| File | Description |
|---|---|
cluster-plan.json |
Machine-readable cluster plan (full data) |
cluster-plan.md |
Human-readable cluster plan summary |
cluster-map.html |
Interactive SVG visualization |
cluster-briefs/ |
Content briefs (if no claude-blog) |
cluster-scorecard.md |
Post-execution quality report |
Cross-Skill Integration
| Skill | Relationship |
|---|---|
seo-plan |
Import source: strategy import reads seo-plan output |
seo-content |
Quality check: E-E-A-T validation of generated content |
seo-schema |
Schema markup: Article, BreadcrumbList, ItemList for cluster pages |
seo-dataforseo |
Data source: SERP data when DataForSEO MCP is available |
seo-google |
Reporting: generate PDF report of cluster plan and scorecard |
After cluster planning or execution completes, offer:
"Generate a PDF report? Use /seo google report"
Error Handling
| Error | Cause | Resolution |
|---|---|---|
| "No seed keyword provided" | Missing argument | Prompt user for seed keyword or URL |
| "Insufficient keyword variants" | Expansion yielded < 15 keywords | Run second expansion pass with PAA questions |
| "SERP data unavailable" | WebSearch and DataForSEO both failing | Retry after 30s; if persistent, use intent-only clustering with warning |
| "No strategy file found" | --from strategy but no plan exists |
Prompt user to run /seo plan first |
| "cluster-plan.json not found" | Execute without planning | Prompt user to run /seo cluster plan first |
| "claude-blog not installed" | Execute attempted without blog skill | Generate content briefs instead; suggest installation |
| "DataForSEO budget exceeded" | Cost check returned "blocked" | Fall back to WebSearch; inform user |
| "Duplicate primary keywords" | Cannibalization detected | Merge affected posts or reassign keywords |
| "Orphan page detected" | Post missing incoming links | Add links from nearest cluster siblings |
| "Resume state corrupted" | Mismatch between plan and output | Rebuild state from output directory scan |
Security
- All URLs fetched via
"${CLAUDE_PLUGIN_ROOT}/scripts/claude-seo" run render_page.py <url> --mode auto(SPA-aware SSRF protection viaurl_safety) - No credentials stored or transmitted
- Output files contain no PII or API keys
- DataForSEO cost checks run before every API call
FLOW Framework Integration
For prompt-guided keyword research and gap analysis, use /seo flow find [url|topic]: FLOW's 5 find-stage prompts complement the SERP-overlap clustering methodology with structured discovery prompts.
| 1 | |
| 2 | name seo-cluster |
| 3 | description > |
| 4 | SERP-based semantic topic clustering for content architecture planning. Groups |
| 5 | keywords by actual Google SERP overlap (not text similarity), designs hub-and-spoke |
| 6 | content clusters with internal link matrices, and generates interactive |
| 7 | visualizations. Optionally executes content creation if claude-blog is installed. |
| 8 | Use when user says "topic cluster", "content cluster", "semantic clustering", |
| 9 | "pillar page", "hub and spoke", "content architecture", "keyword grouping", |
| 10 | or "cluster plan". |
| 11 | user-invocable true |
| 12 | argument-hint "<seed-keyword or url>" |
| 13 | license MIT |
| 14 | metadata |
| 15 | author AgriciDaniel |
| 16 | original_author "Lutfiya Miller (Pro Hub Challenge Winner)" |
| 17 | version "2.3.1" |
| 18 | category seo |
| 19 | |
| 20 | |
| 21 | # Semantic Topic Clustering |
| 22 | |
| 23 | SERP-overlap-driven keyword clustering for content architecture. Groups keywords |
| 24 | by how Google actually ranks them (shared top-10 results), not by text similarity. |
| 25 | Designs hub-and-spoke content clusters with internal link matrices and generates |
| 26 | interactive cluster map visualizations. |
| 27 | |
| 28 | **Scripts:** Located at the plugin root `scripts/` directory. |
| 29 | |
| 30 | |
| 31 | |
| 32 | ## Quick Reference |
| 33 | |
| 34 | | Command | What it does | |
| 35 | |---------|-------------| |
| 36 | | `/seo cluster plan <seed-keyword>` | Full planning workflow: expand, cluster, architect, visualize | |
| 37 | | `/seo cluster plan --from strategy` | Import from existing `/seo plan` output | |
| 38 | | `/seo cluster execute` | Execute plan: create content via claude-blog or output briefs | |
| 39 | | `/seo cluster map` | Regenerate the interactive cluster visualization | |
| 40 | |
| 41 | |
| 42 | |
| 43 | ## Planning Workflow |
| 44 | |
| 45 | ### Step 1: Seed Keyword Expansion |
| 46 | |
| 47 | Expand the seed keyword into 30-50 variants using WebSearch: |
| 48 | |
| 49 | **Related searches**: Search the seed, extract "related searches" and "people also search for" |
| 50 | **People Also Ask (PAA)**: Extract all PAA questions from SERP results |
| 51 | **Long-tail modifiers**: Append common modifiers: "best", "how to", "vs", "for beginners", "tools", "examples", "guide", "template", "mistakes", "checklist" |
| 52 | **Question mining**: Generate who/what/when/where/why/how variants |
| 53 | **Intent modifiers**: Add commercial modifiers: "pricing", "review", "alternative", "comparison", "free", "top" |
| 54 | |
| 55 | **Deduplication:** Normalize variants (lowercase, strip articles), remove exact duplicates. |
| 56 | Target: 30-50 unique keyword variants. If under 30, run a second expansion pass |
| 57 | with the top PAA questions as seeds. |
| 58 | |
| 59 | ### Step 2: SERP Overlap Clustering |
| 60 | |
| 61 | This is the core differentiator. Load `references/serp-overlap-methodology.md` for |
| 62 | the full algorithm. |
| 63 | |
| 64 | **Process:** |
| 65 | Group keywords by initial intent guess (reduces pairwise comparisons) |
| 66 | For each candidate pair within a group, WebSearch both keywords |
| 67 | Count shared URLs in the top 10 organic results (ignore ads, featured snippets, PAA) |
| 68 | Apply thresholds: |
| 69 | |
| 70 | | Shared Results | Relationship | Action | |
| 71 | |---------------|-------------|--------| |
| 72 | | 7-10 | Same post | Merge into single target page | |
| 73 | | 4-6 | Same cluster | Group under same spoke cluster | |
| 74 | | 2-3 | Interlink | Place in adjacent clusters, add cross-links | |
| 75 | | 0-1 | Separate | Assign to different clusters or exclude | |
| 76 | |
| 77 | **Optimization:** With 40 keywords, full pairwise = 780 comparisons. Instead: |
| 78 | Pre-group by intent (4 groups of ~10 = 4 x 45 = 180 comparisons) |
| 79 | Only cross-check group boundary keywords |
| 80 | Skip pairs where both are long-tail variants of the same head term (assume same cluster) |
| 81 | |
| 82 | **DataForSEO integration:** If DataForSEO MCP is available, use `serp_organic_live_advanced` |
| 83 | instead of WebSearch for SERP data. Run `"${CLAUDE_PLUGIN_ROOT}/scripts/claude-seo" run dataforseo_costs.py check serp_organic_live_advanced --count N` |
| 84 | before each batch. If `"status": "needs_approval"`, show cost estimate and ask user. |
| 85 | If `"status": "blocked"`, fall back to WebSearch. |
| 86 | |
| 87 | ### Step 3: Intent Classification |
| 88 | |
| 89 | Classify each keyword into one of four intent categories: |
| 90 | |
| 91 | | Intent | Signals | Include in Clusters? | |
| 92 | |--------|---------|---------------------| |
| 93 | | Informational | how, what, why, guide, tutorial, learn | Yes | |
| 94 | | Commercial | best, top, review, comparison, vs, alternative | Yes | |
| 95 | | Transactional | buy, price, discount, coupon, order, sign up | Yes | |
| 96 | | Navigational | brand names, specific product names, login | No (exclude) | |
| 97 | |
| 98 | Remove navigational keywords from clustering. Flag borderline cases for |
| 99 | manual review. Keywords can have mixed intent (e.g., "best CRM software" is |
| 100 | both commercial and informational) -- classify by dominant intent. |
| 101 | |
| 102 | ### Step 4: Hub-and-Spoke Architecture |
| 103 | |
| 104 | Load `references/hub-spoke-architecture.md` for full specifications. |
| 105 | |
| 106 | **Design the cluster structure:** |
| 107 | |
| 108 | **Select the pillar keyword**: Highest volume, broadest intent, most SERP overlap with other keywords |
| 109 | **Group spokes into clusters**: Each cluster is a subtopic area (2-5 clusters per pillar) |
| 110 | **Assign posts to clusters**: Each cluster gets 2-4 spoke posts |
| 111 | **Select templates per post**: Based on intent classification: |
| 112 | |
| 113 | | Intent Pattern | Template Options | |
| 114 | |---------------|-----------------| |
| 115 | | Informational (broad) | ultimate-guide | |
| 116 | | Informational (how) | how-to | |
| 117 | | Informational (list) | listicle | |
| 118 | | Informational (concept) | explainer | |
| 119 | | Commercial (compare) | comparison | |
| 120 | | Commercial (evaluate) | review | |
| 121 | | Commercial (rank) | best-of | |
| 122 | | Transactional | landing-page | |
| 123 | |
| 124 | **Set word count targets:** |
| 125 | Pillar page: 2500-4000 words |
| 126 | Spoke posts: 1200-1800 words |
| 127 | |
| 128 | **Cannibalization check**: No two posts share the same primary keyword. If SERP |
| 129 | overlap is 7+, merge those keywords into a single post targeting both. |
| 130 | |
| 131 | ### Step 5: Internal Link Matrix |
| 132 | |
| 133 | Design the bidirectional linking structure: |
| 134 | |
| 135 | | Link Type | Direction | Requirement | |
| 136 | |-----------|-----------|-------------| |
| 137 | | Spoke to pillar | spoke -> pillar | Mandatory (every spoke) | |
| 138 | | Pillar to spoke | pillar -> spoke | Mandatory (every spoke) | |
| 139 | | Spoke to spoke (within cluster) | spoke <-> spoke | 2-3 links per post | |
| 140 | | Cross-cluster | spoke -> spoke (other cluster) | 0-1 links per post | |
| 141 | |
| 142 | **Rules:** |
| 143 | Every post must have minimum 3 incoming internal links |
| 144 | No orphan pages (every post reachable from pillar in 2 clicks) |
| 145 | Anchor text must use target keyword or close variant (no "click here") |
| 146 | Link placement: within body content, not just navigation/sidebar |
| 147 | |
| 148 | Generate the link matrix as a JSON adjacency list: |
| 149 | |
| 150 | { |
| 151 | "links": [ |
| 152 | { "from": "pillar", "to": "cluster-0-post-0", "type": "mandatory", "anchor": "keyword" }, |
| 153 | { "from": "cluster-0-post-0", "to": "pillar", "type": "mandatory", "anchor": "keyword" } |
| 154 | ] |
| 155 | } |
| 156 | |
| 157 | |
| 158 | ### Step 6: Interactive Cluster Map |
| 159 | |
| 160 | Generate `cluster-map.html` using the template at `templates/cluster-map.html`. |
| 161 | |
| 162 | Read the template file |
| 163 | Build the `CLUSTER_DATA` JSON object from the cluster plan: |
| 164 | |
| 165 | { |
| 166 | pillar: { title, keyword, volume, template, wordCount, url }, |
| 167 | clusters: [{ name, color, posts: [{ title, keyword, volume, template, wordCount, url, status }] }], |
| 168 | links: [{ from, to, type }], |
| 169 | meta: { totalPosts, totalClusters, totalLinks, estimatedWords } |
| 170 | } |
| 171 | |
| 172 | Replace the `CLUSTER_DATA` placeholder in the template with the actual JSON |
| 173 | Write the completed HTML file to the output directory |
| 174 | Inform user: "Open `cluster-map.html` in a browser to explore the interactive cluster map." |
| 175 | |
| 176 | |
| 177 | |
| 178 | ## Strategy Import |
| 179 | |
| 180 | When invoked with `--from strategy`: |
| 181 | |
| 182 | Look for the most recent `/seo plan` output in the current directory (search for |
| 183 | files matching `*SEO*Plan*`, `*strategy*`, `*content-strategy*`) |
| 184 | Parse markdown tables for: keywords, page types, content pillars, URL structures |
| 185 | Validate extracted data: check for duplicates, missing keywords, incomplete entries |
| 186 | Enrich with SERP data: run SERP overlap analysis on extracted keywords |
| 187 | Build cluster plan using the imported keywords as the starting set (skip Step 1) |
| 188 | |
| 189 | If no strategy file is found, prompt the user: "No existing SEO plan found in the |
| 190 | current directory. Run `/seo plan` first, or provide a seed keyword for fresh clustering." |
| 191 | |
| 192 | |
| 193 | |
| 194 | ## Execution Workflow |
| 195 | |
| 196 | When `/seo cluster execute` is invoked: |
| 197 | |
| 198 | ### Check for claude-blog |
| 199 | |
| 200 | |
| 201 | Test: Does ~/.claude/skills/blog/SKILL.md exist? |
| 202 | |
| 203 | |
| 204 | **If claude-blog IS installed:** |
| 205 | |
| 206 | Load `references/execution-workflow.md` for the full algorithm |
| 207 | Read `cluster-plan.json` from the current directory |
| 208 | Check for resume state: scan output directory for already-written posts |
| 209 | Execute in priority order: pillar first, then spokes by volume (highest first) |
| 210 | For each post, invoke the `blog-write` skill with cluster context: |
| 211 | Cluster role (pillar or spoke) |
| 212 | Position in cluster (cluster index, post index) |
| 213 | Target keyword and secondary keywords |
| 214 | Template type and word count target |
| 215 | Internal links to include (with anchors) |
| 216 | Links to receive from future posts (placeholder markers) |
| 217 | After each post is written, scan previous posts for backward link placeholders |
| 218 | and inject the new post's URL |
| 219 | After all posts are written, generate the cluster scorecard |
| 220 | |
| 221 | **If claude-blog is NOT installed:** |
| 222 | |
| 223 | Generate detailed content briefs for each post in the cluster plan |
| 224 | Each brief includes: |
| 225 | Title and meta description |
| 226 | Primary keyword and secondary keywords |
| 227 | Template type and suggested structure (H2/H3 outline) |
| 228 | Word count target |
| 229 | Internal links to include (with anchor text) |
| 230 | Key points to cover |
| 231 | Competing pages to differentiate from |
| 232 | Write briefs to `cluster-briefs/` directory as individual markdown files |
| 233 | Inform user: "Install [claude-blog] |
| 234 | to auto-create content. Briefs saved to `cluster-briefs/`." |
| 235 | |
| 236 | |
| 237 | |
| 238 | ## Cluster Scorecard |
| 239 | |
| 240 | Post-execution quality report. Run automatically after `/seo cluster execute` or |
| 241 | on demand via analysis of the output directory. |
| 242 | |
| 243 | | Metric | Target | How Measured | |
| 244 | |--------|--------|-------------| |
| 245 | | Coverage | 100% | Posts written / posts planned | |
| 246 | | Link Density | 3+ per post | Count internal links per post | |
| 247 | | Orphan Pages | 0 | Posts with < 1 incoming link | |
| 248 | | Cannibalization | 0 conflicts | Check for duplicate primary keywords | |
| 249 | | Image Count | 1+ per post | Posts with at least one image | |
| 250 | | Pillar Links | 100% | All spokes link to pillar and vice versa | |
| 251 | | Cross-Links | 80%+ | Recommended spoke-to-spoke links implemented | |
| 252 | | Content Gaps | 0 | Planned posts that were skipped or incomplete | |
| 253 | |
| 254 | |
| 255 | |
| 256 | ## Map Regeneration |
| 257 | |
| 258 | When `/seo cluster map` is invoked: |
| 259 | |
| 260 | Read `cluster-plan.json` from the current directory |
| 261 | Scan output directory and update post statuses (planned vs written) |
| 262 | Regenerate `cluster-map.html` with updated statuses |
| 263 | Report: posts written vs planned, link completion percentage |
| 264 | |
| 265 | |
| 266 | |
| 267 | ## Output Files |
| 268 | |
| 269 | All outputs are written to the current working directory: |
| 270 | |
| 271 | | File | Description | |
| 272 | |------|-------------| |
| 273 | | `cluster-plan.json` | Machine-readable cluster plan (full data) | |
| 274 | | `cluster-plan.md` | Human-readable cluster plan summary | |
| 275 | | `cluster-map.html` | Interactive SVG visualization | |
| 276 | | `cluster-briefs/` | Content briefs (if no claude-blog) | |
| 277 | | `cluster-scorecard.md` | Post-execution quality report | |
| 278 | |
| 279 | |
| 280 | |
| 281 | ## Cross-Skill Integration |
| 282 | |
| 283 | | Skill | Relationship | |
| 284 | |-------|-------------| |
| 285 | | `seo-plan` | Import source: strategy import reads seo-plan output | |
| 286 | | `seo-content` | Quality check: E-E-A-T validation of generated content | |
| 287 | | `seo-schema` | Schema markup: Article, BreadcrumbList, ItemList for cluster pages | |
| 288 | | `seo-dataforseo` | Data source: SERP data when DataForSEO MCP is available | |
| 289 | | `seo-google` | Reporting: generate PDF report of cluster plan and scorecard | |
| 290 | |
| 291 | After cluster planning or execution completes, offer: |
| 292 | "Generate a PDF report? Use `/seo google report`" |
| 293 | |
| 294 | |
| 295 | |
| 296 | ## Error Handling |
| 297 | |
| 298 | | Error | Cause | Resolution | |
| 299 | |-------|-------|------------| |
| 300 | | "No seed keyword provided" | Missing argument | Prompt user for seed keyword or URL | |
| 301 | | "Insufficient keyword variants" | Expansion yielded < 15 keywords | Run second expansion pass with PAA questions | |
| 302 | | "SERP data unavailable" | WebSearch and DataForSEO both failing | Retry after 30s; if persistent, use intent-only clustering with warning | |
| 303 | | "No strategy file found" | `--from strategy` but no plan exists | Prompt user to run `/seo plan` first | |
| 304 | | "cluster-plan.json not found" | Execute without planning | Prompt user to run `/seo cluster plan` first | |
| 305 | | "claude-blog not installed" | Execute attempted without blog skill | Generate content briefs instead; suggest installation | |
| 306 | | "DataForSEO budget exceeded" | Cost check returned "blocked" | Fall back to WebSearch; inform user | |
| 307 | | "Duplicate primary keywords" | Cannibalization detected | Merge affected posts or reassign keywords | |
| 308 | | "Orphan page detected" | Post missing incoming links | Add links from nearest cluster siblings | |
| 309 | | "Resume state corrupted" | Mismatch between plan and output | Rebuild state from output directory scan | |
| 310 | |
| 311 | |
| 312 | |
| 313 | ## Security |
| 314 | |
| 315 | All URLs fetched via `"${CLAUDE_PLUGIN_ROOT}/scripts/claude-seo" run render_page.py <url> --mode auto` (SPA-aware SSRF protection via `url_safety`) |
| 316 | No credentials stored or transmitted |
| 317 | Output files contain no PII or API keys |
| 318 | DataForSEO cost checks run before every API call |
| 319 | |
| 320 | ## FLOW Framework Integration |
| 321 | |
| 322 | For prompt-guided keyword research and gap analysis, use `/seo flow find [url|topic]`: FLOW's 5 find-stage prompts complement the SERP-overlap clustering methodology with structured discovery prompts. |
| 323 |
Discussion
From GitHub
2 comments on 2 threadsFixed in #261 and released in v2.2.5. User-facing skill and agent instructions now invoke bundled tools through `claude-seo run`, the consistency gate rejects raw `scripts/*.py` references, and manual installers rewrite commands to the isolated launcher path. This removes both plugin-root discovery and system-Python dependency assumptions. Closing as resolved.