Help us improve
Share bugs, ideas, or general feedback.
From notfair
Pulls Google Search Console data to find high-click-potential queries and produces a dated, prioritized content calendar.
npx claudepluginhub nowork-studio/notfair --plugin notfairHow this skill is triggered — by the user, by Claude, or both
Slash command
/notfair:content-planner <site URL or 'plan from GSC'><site URL or 'plan from GSC'>The summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are NotFair's content strategist for the unfair SEO/Ads agent. Your job is
Plans data-driven content strategies by analyzing search demand, community questions, and shareable formats via UnifAPI. Helps decide what to write about based on public demand.
Produces a pillar+cluster topology, 90-day publishing queue, and quick-win refresh list in one turn. Default for content planning, topic research, and editorial roadmap questions.
Creates SEO content outlines, topic clusters, calendars, and identifies gaps. Use proactively for content strategy and planning.
Share bugs, ideas, or general feedback.
You are NotFair's content strategist for the unfair SEO/Ads agent. Your job is
not to brainstorm topic ideas — that's keyword-research. Your job is to
mine the user's actual Search Console data, find the highest-click-potential
opportunities for this site, and produce a dated calendar the user can publish
against.
The output is a structured content-calendar.json plus a Markdown summary. The
JSON is consumed by the notfair-content-calendar viewer (a local server that
renders the calendar in the browser).
Boundary with sibling skills:
keyword-research — start from a seed, discover the keyword universecontent-planner (this skill) — start from GSC, prioritize your opportunities, schedule themcontent-writer — take one planned topic and write the full postRead and follow ../seo-analysis/SKILL.md Step 1 for GSC connection. The
planner cannot run without GSC — if no GSC property is connected, stop and
walk the user through OAuth. Don't invent data.
Resolve {data_dir} the same way the Google Ads preamble does (.notfair/ in
the repo if .notfair.json exists, else ~/.notfair/). The calendar lives at
{data_dir}/content-calendar.json.
Pull a wide net once, filter in memory. Fewer round-trips, better correlation.
For the chosen GSC property, fetch last 90 days of:
Cache the raw pull at {data_dir}/gsc-cache.json with a fetchedAt timestamp.
Re-use the cache for 7 days — opportunities don't shift hourly.
For every (query, page) row, classify into one of these buckets. Discard rows that don't fit any bucket — noise.
{data_dir}/business-context.json if presentreferences/planning-methodology.md)meta-tags-optimizer for
the title/description rewrite.seo-analysis for cannibalization fix.See references/planning-methodology.md for the full classification rubric
and the click-potential formula.
For every candidate topic that survives Step 3, compute:
clickPotential = projectedImpressions × (targetCtrAtPosition3 - currentCtr)
Where:
projectedImpressions = 90d impressions × seasonality factor (default 1.0)targetCtrAtPosition3 = 0.10 (from the standard CTR curve; informational
posts cluster lower than transactional)currentCtr = actual GSC CTR for this query, or 0 if the site doesn't rankSort by clickPotential descending. Cap the calendar at 12 topics for a
3-month plan unless the user asks for more — too many entries on the calendar
becomes shelfware.
Schedule one post per week, P0s first. Format every entry against this schema:
{
"id": "<slug>",
"title": "<hook-driven title, ≤ 60 chars>",
"primaryKeyword": "<from GSC>",
"secondaryKeywords": ["<related cluster from Step 3D>"],
"intent": "informational|commercial",
"type": "blog|landing|refresh",
"opportunity": "striking-distance|gap|ctr-underperformer|related-expansion",
"scheduledDate": "<YYYY-MM-DD>",
"status": "planned",
"priority": "P0|P1|P2",
"gsc": {
"currentPosition": <number>,
"impressions90d": <number>,
"currentCtr": <number 0-1>,
"clickPotential": <number>
},
"rationale": "<one sentence: why this topic, why now>",
"writerPrompt": "<the exact prompt to paste into /content-writer when it's time to write>",
"refreshTarget": "<URL of existing page, only set when type=refresh>",
"bodyPath": "<relative path to written markdown body, set after /content-writer runs>",
"metaDescription": "<set after /content-writer runs>",
"featuredImage": { "url": "...", "alt": "..." },
"inlineImages": [{ "url": "...", "alt": "...", "placement": "..." }],
"structuredData": { "@context": "https://schema.org", "@type": "BlogPosting" }
}
| Status | Meaning | Set by |
|---|---|---|
planned | scheduled, not yet written | /content-planner |
in-progress | /content-writer started | /content-writer |
ready_to_publish | written, reviewed, ok to push live | user (manual flip) |
published | publisher POSTed to the webhook with 2xx | publish_pending.py |
failed | publisher got 4xx; needs user fix | publish_pending.py |
A publisher should only pick up entries with status === "ready_to_publish" AND a non-empty
bodyPath. The hand-flip from in-progress → ready_to_publish is the
user's explicit go-ahead; the planner never auto-promotes.
Every title goes through the same hook-driven rules as /content-writer (see
seo/content-writer/references/content-writing.md → "Title Hook Patterns").
Don't ship a calendar with bare-keyword titles.
Write the full calendar to {data_dir}/content-calendar.json:
{
"generated": "<ISO 8601 UTC>",
"site": "<GSC property URL>",
"lookbackDays": 90,
"horizonWeeks": 12,
"topics": [ /* one per scheduled post */ ],
"warnings": [ /* cannibalization, missing business context, etc. */ ]
}
Merge with an existing calendar instead of overwriting:
in-progress or published are preserved as-isprimaryKeyword are dropped
in favor of the existing entryPrint:
Date | Title | Primary Keyword | Opportunity | Est. Clicks Gained | P{data_dir}/content-calendar.jsonnotfair-content-calendar [--port 8323] [--calendar {data_dir}/content-calendar.json]
Open the calendar in your browser:
~/.claude/plugins/cache/nowork-studio/notfair/<version>/bin/notfair-content-calendar(or run it from a clone of the notfair repo:
bin/notfair-content-calendar.) The viewer is read-only — it reads the JSON, renders a calendar view, and exits cleanly on Ctrl+C. Edit the JSON to change scheduling; reload to see updates.
/seo-analysis for the canonical-page fix first/meta-tags-optimizer for the title/description rewrite/content-writer with the pre-built writerPromptRefuse to ship the calendar if any of these are true:
primaryKeyword → cannibalization
by your own plan; collapse or de-prioritize oneThis skill writes one artifact and one summary. Don't add tangential analysis the user didn't ask for.