Cluster
Claude Code workspace template for running consistent SEO across a portfolio of managed websites.
npx -y skills add mkreindel/seo-toolkit-template --skill clusterAssembled 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.
What its author says it does
Copied from the file, not written here
Plan a topic cluster around a seed keyword for a chosen managed site. Pulls 5-15 SERP-clustered related keywords (per SEO_GUIDE Section 2.4 overlap test), designs a pillar page (broad target) + N cluster pages (narrow targets), specifies the internal-linking graph (pillar ↔ clusters + lateral cluster ↔ cluster), and generates wireframes for every page in one pass. Output lands in `_drafts/cluster-{seed-slug}/` ready for `/blog` or `/service` to fill in content. Use when the user types `/cluster {seed-keyword} {site}` or asks for a topic-cluster plan.
SKILL.md
17.7 KB, as published. Nobody here has run it
/cluster — Topic cluster planner
Plans a complete topic cluster around a seed keyword: pillar page + N cluster pages + the internal-linking graph + wireframes for every page. Topical authority is built at cluster granularity, not single-post granularity — and this skill is what makes that engineering possible inside the toolkit.
Inputs
Required (asked at start if not given):
- Seed keyword — the broad topical anchor. Examples: "ai consultant" (site-a), "spanish catering houston" (site-b), "endocrinologist houston" (site-c).
- Site — must match a folder under
sites/[name]/. The skill reads itssite-info.md.
Optional (per-run overrides):
--max-clusters=N— caps the cluster count (default 8; valid range 3–15). Lower → faster ship; higher → fuller topical authority but more downstream content to write.--include-bottom-funnel— when set, includes commercial-intent clusters (pricing, vs-competitor, alternative) alongside informational ones. Default: informational-only.- Keyword tool — overrides the site's default (SEMrush, GSC, Ahrefs, manual SERP analysis). Defaults to
site-info.md→Keyword tool.
Workflow
Step 0 — Cron-mode detection (if invoked with --cron)
If the invocation contains --cron, this skill runs in cron mode (no user available). Required behavior:
-
Idempotency check: see this skill's "Routine versioning + idempotency contract" section. If today's output already exists at
_drafts/cluster-{seed-slug}/, exit cleanly withexit: "idempotent-skip". Write one line to the audit log viascripts/lib/audit-log.mjsappendRun({ exit: "idempotent-skip", ... }). -
Escalation contract: any decision that would normally prompt the user (missing required file, voice anti-AI failure, ambiguous SERP cluster, keyword cannibalization) MUST be escalated by writing an item to
sites/{site}/_inbox/viascripts/lib/cron-mode.mjswriteInboxItem(...). After writing, exit cleanly withexit: "escalated". Do NOT useAskUserQuestionin cron mode. -
Defaults: when a choice would normally be asked, default to
site-info.md/goals.mdvalues. If both are silent on the required choice, escalate per (2). -
Audit log: ALWAYS write one line to the audit log on exit — success (
shipped), escalation (escalated), idempotent skip (idempotent-skip), or failure (failed). -
Backoff: at the start of every cron-mode run, call
checkBackoff({ routine })fromscripts/lib/audit-log.mjs. If true, the routine has hit the 3-strike threshold — write_inbox/routine-disabled-{name}.md, runscripts/sync-schedules.mjs --pause-routine={name}, and exit.
Step 1 — Read context (Rule 1)
Load in order:
CLAUDE.md(toolkit root) — especially# Multilingual sitesand# Keyword researchsectionsSEO_GUIDE.md— Section 2.4 (SERP comparison test), Section 3.3 (cluster design), Section 5.1 (internal-linking patterns), Section 2.6 (architecture)on-page-seo.md— Tier 1 reference (each cluster page eventually satisfies all 16)sites/[site]/site-info.md— Languages block + footprint + Service business + Architecture pointersites/[site]/architecture.mdif present (required for service-business sites with >3 commercial keywords)- Resolved
references/— root files + per-site overrides sites/[site]/keywords.csvandsites/[site]/service-keywords.csvsites/[site]/used-keywords.mdsites/[site]/notes.md
If any required file is missing, ask the user before proceeding.
Step 2 — Validate seed + intent
The seed keyword anchors the entire cluster. Validate:
-
Intent classification. Run a quick SERP look at the seed:
- If top 10 is dominated by informational content (definitional posts, how-to guides, "what is X" pages) → informational cluster. Cluster pages mostly become blog posts.
- If top 10 is dominated by commercial pages (service offerings, product pages, comparison/pricing) → commercial cluster. Cluster pages become service pages.
- If mixed → flag and ask user which intent dominates this site's positioning before proceeding.
-
Cannibalization check. If the seed (or close variants) already appears in
used-keywords.mdorkeywords.csvas the primary keyword of an existing/planned page, abort and report. Topic clusters cannot be built around a seed that's already been "spent" on a single page — the existing page must be retargeted as the pillar, OR a different seed picked. -
Architecture-fit check (commercial seeds only). If site is service-business with architecture.md, the seed must already appear in architecture as a pillar candidate, OR the user adds it to architecture before generating. Off-architecture clusters create cannibalization at scale.
Step 3 — SERP-cluster expansion
Goal: surface 5-15 keywords that share enough SERP overlap with the seed to belong on the same cluster.
-
Pull related keywords from the site's chosen keyword tool:
- SEMrush:
scripts/semrush.mjs --keyword="{seed}" --type=related(orphrase_relatedendpoint). Returns 50-200 related terms with volume + KD. - GSC fallback: if SEMrush unavailable, pull GSC top-queries where the site already shows up for variants of the seed (
scripts/keyword-discovery.mjs --site={site} --grep="{seed-stem}"). - Manual fallback: pull PAA (People Also Ask) from Google + autocomplete suggestions.
- SEMrush:
-
SERP-overlap test (per
SEO_GUIDE.mdSection 2.4) on each candidate vs the seed:- Fetch top 10 for both incognito (target region per
site-info.md). - Count shared URLs.
- ≥ 4 shared → cluster member (high confidence)
- 2–3 shared → borderline (keep only if KD is attainable and volume ≥ 100)
- ≤ 1 shared → exclude (belongs on a different page, suggest as future seed)
- Fetch top 10 for both incognito (target region per
-
Attainability filter. Each candidate must satisfy
KD ≤ site_AS + 15(the realism filter from v2 spec Hard Truth 2). Where:site_AS= latest SEMrush Authority Score fromsites/{site}/_baselines/semrush-*.json- Candidates above the threshold are flagged as "stretch" — included only if user explicitly opts in.
-
Volume cap. Cluster members should each have volume ≥ 50/mo (lower than the 100 floor for individual blog posts because clusters create compounding value through linking — even small-volume terms contribute to topical authority).
-
Cap the final list at
--max-clusters(default 8). Rank byvolume × (1 / KD_distance)and take the top N.
Step 4 — Design the cluster shape
For each member of the cluster:
-
Assign role. One of:
- Pillar (broadest seed) — the topical hub. Single page. Title matches the seed. Designed to rank for the broad term + serve as the entry point.
- Cluster page (specific aspect) — N pages. Each covers one narrower angle (definition, how-to, vs-comparison, pricing, use-case, etc.).
- Optional spoke (very narrow long-tail) — only if --max-clusters allows.
-
Assign intent type. Per page:
info— informational, becomes a blog post (handled by/bloglater)comm— commercial, becomes a service page (handled by/servicelater)mixed— hybrid, becomes a blog post that bridges into commercial pages
-
Title / H1 / slug. Per page:
- Title: 50-60 chars, primary keyword near start
- H1: sentence case, contains the primary keyword
- Slug: short, lowercase, hyphenated, contains primary keyword stem
-
Per-language fan-out (multilingual sites): each cluster page gets its EN + ES (or other declared language) variant. Slug is shared; titles/H1s are native rewrites per
voice.[lang].md.
Step 5 — Internal-linking graph design
The link graph is the load-bearing structure that converts N independent pages into a topical cluster.
-
Pillar links to ALL clusters. Every cluster page is linked from the pillar via inline anchor text (3-5 internal links per pillar section, each pointing to a relevant cluster).
-
Every cluster links back to pillar. Each cluster page has at minimum 1 "see the pillar" link in body copy, usually in the introduction or a "for the full overview, see [pillar]" callout.
-
Lateral cluster links based on SERP overlap:
- For each pair of cluster pages, if they share ≥ 4 SERP URLs with each other → bidirectional internal link.
- If they share 2-3 → unidirectional from the higher-volume one to the lower-volume one (lower one borrows authority).
- If they share ≤ 1 → no lateral link.
-
Anchor text variation. Use varied anchor text across all links — never link the same target with the same anchor 3+ times across the cluster. Mix exact-match (the primary keyword), partial-match (variant), and natural-language (descriptive phrase).
-
External links (cluster-wide consistency): each page has 2-3 external links to authoritative sources. Across the cluster, those sources should overlap by 30-60% (signals topical consistency to Google).
Step 6 — Wireframe generation
For the pillar AND each cluster page, generate a wireframe at the path defined in Step 9. Each wireframe follows the standard shape from .claude/skills/wireframe/SKILL.md and CLAUDE.md "Wireframe before content" rule, with cluster-aware additions:
Required wireframe sections (per page):
- Layout zones (top-to-bottom): hero, TOC if 1500+ words, content sections (H2 per section), FAQ, related-cluster (the lateral links), author bio (blog) or NAP (service).
- Heading map (H1 / H2 / H3) — H1 = primary keyword for this page; H2s ordered by what the SERP top 3 use as their structure (overlap > divergence).
- Internal linking pattern slots (per
SEO_GUIDE.mdSection 5.1):- For pillar: patterns 1 (header dropdown), 2 (TOC), 3 (footer), 4 (breadcrumb), 7 (blog→service bridges to relevant cluster pages)
- For cluster: patterns 1, 3, 4, 6 (related-cluster sidebar/end-section), 7 (back to pillar)
- Image plan — hero image + N inline images.
- External link plan — 2-3 authoritative sources by topic.
- CTA placement — where the conversion ask sits.
- Cluster-specific section — explicit list of which other cluster pages this page should link to (the lateral graph from Step 5).
Step 7 — Plan + approval (Rule 2)
Present to user before writing any files:
- Seed + intent classification + dominant SERP shape
- Pillar page proposal: title + slug + intent
- Cluster page list: N rows of (keyword + title + slug + intent + role)
- Internal-link graph: ASCII or matrix view (pillar ↔ each cluster + lateral pairs)
- Language fan-out (multilingual): list of languages × pages = total file count
- Estimated downstream effort: N pages × ~1-2h per page (via
/blogor/service) = total ramp-up time - Architecture impact (commercial clusters): if pillar/clusters need to be added to
architecture.md, list them
Wait for explicit approval. If user wants to reduce N → re-run Step 3-5 with smaller --max-clusters.
Step 8 — Resolve language fan-out (multilingual sites only)
Read site-info.md Languages section.
Multilingual: false→ skip; continue to Step 9 as a single-language cluster.Multilingual: true:- Default coverage = every language declared.
- For each declared language:
- Resolve voice files: root
references/voice.md+ per-sitereferences/voice.[lang].md(REQUIRED). - Cluster slug shared across languages.
- Hreflang cluster generated for every page (en + es + x-default).
- URLs formatted per language URL pattern from site-info.md.
- Resolve voice files: root
- Schema
inLanguageper language draft.
The skill produces 1 cluster × N languages = N×(pillar + clusters) wireframe files total. Each in its own _drafts/cluster-{seed-slug}/{lang}/ subfolder.
Step 9 — Write output
Create the directory tree:
sites/{site}/_drafts/cluster-{seed-slug}/
├── cluster-plan.md # overview + keyword assignments + link graph
├── {lang}/ # per-language (only if multilingual)
│ ├── pillar/
│ │ └── wireframe.md
│ └── clusters/
│ ├── {cluster-1-slug}/
│ │ └── wireframe.md
│ ├── {cluster-2-slug}/
│ │ └── wireframe.md
│ └── ... (N total)
Monolingual sites skip the {lang}/ directory layer:
sites/{site}/_drafts/cluster-{seed-slug}/
├── cluster-plan.md
├── pillar/wireframe.md
└── clusters/{slug}/wireframe.md # N of these
cluster-plan.md contents:
# Cluster plan — {site} — {seed}
**Created:** {YYYY-MM-DD}
**Seed keyword:** {seed}
**Intent classification:** {info | comm | mixed}
**Site AS at plan time:** {N}
**Attainability ceiling (KD ≤ site_AS + 15):** {N+15}
**Languages:** {en, es, ...}
## Pillar page
- **URL:** {site-origin}/{lang-prefix}/{path}/{slug}
- **Primary keyword:** {pillar-keyword}
- **Title:** {pillar-title}
- **H1:** {pillar-h1}
- **Intent:** {info|comm|mixed}
- **Volume:** {volume}/mo
- **KD:** {kd}
- **Wireframe:** ./pillar/wireframe.md (or ./{lang}/pillar/wireframe.md)
## Cluster pages
| # | Keyword | URL | Title | Intent | Vol | KD | Wireframe |
|---|---------|-----|-------|--------|-----|-----|-----------|
| 1 | {kw} | ... | ... | info | ... | ... | ./clusters/{slug}/wireframe.md |
| 2 | ... | ... | ... | ... | ... | ... | ... |
... (N rows)
## Internal-link graph
### Pillar → Clusters (always, 1:N)
- pillar → cluster-1 (anchor: "{anchor-text-1}")
- pillar → cluster-2 (anchor: "{anchor-text-2}")
- ... (N edges)
### Cluster → Pillar (always, N:1)
- cluster-1 → pillar (anchor: "for the full overview…")
- cluster-2 → pillar (anchor: "see the parent guide…")
- ... (N edges)
### Lateral cluster ↔ cluster (per SERP overlap test)
- cluster-1 ↔ cluster-2 (bidirectional; shared SERP URLs: 5)
- cluster-2 → cluster-4 (unidirectional, lower-volume target; shared: 3)
- ... (M edges)
## Next-step build sequence
To ship the cluster, run these commands in order (suggested cadence: 1 per week to let Google crawl/index between):
1. `/blog {site} {pillar-keyword}` ← pillar first; clusters need it as the link target
2. `/blog {site} {cluster-1-keyword}` (or `/service` if intent=comm)
3. `/blog {site} {cluster-2-keyword}` (or `/service`)
... (N more)
Each `/blog` or `/service` run reads `_drafts/cluster-{seed-slug}/clusters/{slug}/wireframe.md` (or the pillar one) for its planning artifact, then generates content following the wireframe + the AI-search optimization rules (P1.5).
## Status
OPEN — awaiting user approval to start the N+1 content runs.
wireframe.md contents (per page): standard wireframe per .claude/skills/wireframe/SKILL.md, with the cluster-specific link slots populated from Step 5's graph.
Step 10 — Update tracker
For each page in the cluster (pillar + N clusters), append a row to sites/{site}/used-keywords.md with status "planned via /cluster — wireframe shipped, content pending."
This prevents future /blog runs from accidentally picking the cluster's keywords as standalone targets. When the content actually ships via /blog or /service, those skills will update the row's status to "shipped."
Step 11 — Report
Print:
- Pillar URL + cluster URLs (N total)
- Cluster plan file path:
sites/{site}/_drafts/cluster-{seed-slug}/cluster-plan.md - Total wireframes generated: N+1 (or (N+1) × num_languages for multilingual)
- Link graph summary: N pillar-to-cluster + N cluster-to-pillar + M lateral edges
- Suggested next-step command:
/blog {site} {pillar-keyword}(start with pillar) - Estimated full cluster shipping timeline: N+1 weeks at 1 post/week cadence
Routine versioning + idempotency contract
This skill participates in cruise-control via --cron mode. When invoked by cron:
-
Stamp
routine_versionin every output produced (cluster-plan.md frontmatter, each wireframe's metadata, audit log line viascripts/lib/audit-log.mjs). Currentroutine_version: 1.0. -
Idempotency key: the cluster slug + seed keyword. If
sites/{site}/_drafts/cluster-{seed-slug}/cluster-plan.mdexists from a prior run with matching seed, exit withidempotent-skip. Do not regenerate (user may have already started content runs against the existing wireframes). -
3-strike contract (per
scripts/lib/audit-log.mjs): three consecutivefailedruns for this skill on a given site → pause automatic invocation, write_inbox/routine-disabled-cluster.md.
Refusal conditions
Refuse and report clearly if:
- Seed keyword already in
used-keywords.mdas a primary target of an existing page (cannibalization risk — must retarget existing page or pick different seed) - Site is service-business with >3 commercial keywords AND
architecture.mdis missing (perSEO_GUIDE.mdSection 2.6) - Site has no
keywords.csvor it's empty (cluster expansion needs candidate pool) - Multilingual site with missing
references/voice.[lang].mdfor any declared language --max-clustersoutside valid range (3-15)- SERP-cluster expansion returns 0 candidates that pass the overlap + attainability filters (in this case, the seed is structurally not a cluster — it's a single-page target; recommend
/blogor/servicedirectly)