
Content Strategy
- 130 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Plan editorial calendars, content pillars, audience messaging, and channel priorities to attract, educate, and convert target users through sustained organic storytelling.
About
Helps define content pillars, editorial calendars, audience messaging, channel priorities, and measurement loops so marketing teams publish cohesive stories that drive sustained acquisition and retention.
- Editorial calendar planning
- Content pillar and topic clusters
- Audience personas and messaging maps
- Channel and format prioritization
- Performance review and iteration loops
Content Strategy by the numbers
- 130 all-time installs (skills.sh)
- Ranked #1,082 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/borghei/claude-skills --skill content-strategyAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 130 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Plan editorial calendars, content pillars, audience messaging, and channel priorities to attract, educate, and convert target users through sustained organic storytelling.
Files
Content Strategy
Strategic content planning that drives traffic, builds authority, and generates leads by being either searchable, shareable, or both.
---
Table of Contents
- Keywords
- Quick Start
- Core Workflows
- Content Pillar Framework
- Audience Research Methodology
- Topic Clustering
- Funnel Mapping
- Content Audit Framework
- Searchable vs. Shareable Matrix
- Competitive Content Analysis
- Best Practices
- Integration Points
---
Keywords
content strategy, content planning, content pillars, topic clusters, content calendar, audience research, content audit, funnel mapping, content gaps, editorial planning, blog strategy, content ideas, topic ideation, SEO content strategy, content roadmap, content marketing strategy, content performance, keyword research, buyer journey content, search intent mapping
---
Quick Start
Build a Content Strategy from Scratch
1. Define 3-5 content pillars aligned with business goals and audience needs 2. Conduct audience research to identify questions, pain points, and language 3. Build topic clusters around each pillar with keyword targets 4. Map topics to funnel stages (awareness, consideration, decision) 5. Prioritize by searchability, business value, and competitive gap 6. Build an editorial calendar with publishing cadence
Audit and Improve Existing Strategy
1. Inventory all existing content with performance metrics 2. Classify each piece by pillar, funnel stage, and format 3. Identify gaps (missing topics, underserved funnel stages, outdated content) 4. Score each piece on traffic, engagement, conversion, and relevance 5. Build remediation plan: update, consolidate, remove, or create new
---
Core Workflows
Workflow 1: Strategy Development
Step 1: Business Context Gathering
Before planning content, understand the business foundation:
## Strategy Context
- Business goal for content: [Traffic / Leads / Brand awareness / Thought leadership]
- Product/service: [What you sell and to whom]
- Ideal customer profile: [Industry, company size, role, seniority]
- Sales cycle: [Self-serve / Sales-assisted / Enterprise]
- Current content state: [Starting fresh / Some content / Mature library]
- Resources: [Writers, budget, publishing capacity per week/month]
- Competitors producing content: [Top 3-5 competitor blogs]Step 2: Customer Research for Content
Mine these sources for content topics:
| Source | What to Extract | How to Use |
|---|---|---|
| Sales call recordings | Questions prospects ask before buying | Create content answering each question |
| Support tickets | Recurring problems and confusion | Tutorial and troubleshooting content |
| Customer interviews | Language they use to describe problems | Use their exact words in headlines and copy |
| Product reviews (yours + competitors) | Praise and complaints | Content addressing concerns, amplifying strengths |
| Community forums (Reddit, Slack, Discord) | Questions, debates, misconceptions | Topic ideas with proven demand |
| Search console queries | What people search to find you | Optimize for highest-potential queries |
Step 3: Define Content Pillars
Pillars are the 3-5 broad topic areas your content focuses on. Each pillar should:
- Connect directly to your product or expertise
- Address a topic your audience actively seeks information about
- Support your business goals (traffic, leads, authority)
- Be broad enough to generate 20+ subtopics
- Be specific enough to establish expertise
Step 4: Build Topic Clusters
For each pillar, develop a cluster of 10-25 topics:
## Pillar: [Topic Area]
Pillar page: [Comprehensive guide serving as the hub]
### Supporting Topics
1. [Topic] — KW: [keyword], Vol: [X], Diff: [X], Intent: [Info/Commercial]
2. [Topic] — KW: [keyword], Vol: [X], Diff: [X], Intent: [Info/Commercial]
3. [Topic] — KW: [keyword], Vol: [X], Diff: [X], Intent: [Info/Commercial]
...
### Internal Linking Plan
- All supporting topics link to pillar page
- Pillar page links to all supporting topics
- Related supporting topics cross-link to each otherStep 5: Prioritize Topics
Score each topic on a priority matrix:
| Factor | Weight | Scoring |
|---|---|---|
| Search volume | 25% | High (3), Medium (2), Low (1) |
| Keyword difficulty | 20% | Easy (3), Medium (2), Hard (1) |
| Business relevance | 30% | Direct product connection (3), Adjacent (2), Tangential (1) |
| Competitive gap | 15% | No good content exists (3), Beatable content (2), Strong competitors (1) |
| Content asset value | 10% | Evergreen + repurposable (3), Seasonal (2), One-time (1) |
Priority score = weighted sum. Execute highest scores first.
Workflow 2: Content Audit
Step 1: Inventory All Content
Build a content inventory spreadsheet:
| URL | Title | Pillar | Format | Publish Date | Last Updated | Monthly Traffic | Avg Time on Page | Conversions | Target Keyword | Ranking Position |
|---|
Step 2: Classify Each Piece
For each piece, determine:
- Pillar alignment: Which pillar does it belong to? (Or does it belong to none?)
- Funnel stage: Awareness, consideration, or decision?
- Content type: Tutorial, comparison, thought leadership, news, case study?
- Status: Current, outdated, thin, duplicate, orphaned?
Step 3: Score Performance
| Score | Traffic | Engagement | Conversion | Action |
|---|---|---|---|---|
| A (top 20%) | High traffic | Above avg time on page | Drives conversions | Protect, repurpose, update regularly |
| B (middle 40%) | Moderate | Average engagement | Some conversions | Optimize, improve, interlink |
| C (next 30%) | Low | Below average | Minimal | Update or consolidate |
| D (bottom 10%) | Near zero | High bounce | None | Redirect, remove, or completely rewrite |
Step 4: Build Remediation Plan
| Action | When | How |
|---|---|---|
| Update | Content is B or C tier with good topic but outdated info | Refresh data, add new sections, update publish date |
| Consolidate | Multiple thin pieces on same topic | Merge into one comprehensive piece, redirect old URLs |
| Remove | Content is irrelevant, duplicate, or unfixable | 301 redirect to most relevant remaining page |
| Create new | Gap exists in pillar coverage | Add to editorial calendar with priority score |
| Optimize | Content ranks #4-20 for target keyword | On-page SEO improvements, better intro, internal links |
---
Content Pillar Framework
Pillar Design Principles
Example Pillar Structure for a SaaS Product:
| Pillar | Business Connection | Audience Need | Content Types |
|---|---|---|---|
| [Core problem you solve] | Direct product relevance | Actively searching for solutions | How-to guides, tutorials, comparisons |
| [Industry your audience works in] | Thought leadership | Staying current on trends | Analysis, predictions, benchmarks |
| [Adjacent skill your audience needs] | Trust building | Professional development | Frameworks, templates, playbooks |
| [Use case deep dives] | Product education | Understanding applications | Case studies, walkthroughs, examples |
Pillar Page Structure
Each pillar should have a comprehensive hub page (2,000-4,000 words) that:
- Defines the topic comprehensively
- Links to all supporting cluster content
- Targets the broadest keyword in the cluster
- Gets updated as new cluster content publishes
- Serves as the authority page for the topic area
---
Audience Research Methodology
Research Framework
| Method | Time Required | Quality | Best For |
|---|---|---|---|
| Customer interview analysis | 2-4 hours | Highest | Understanding language, pain points, objections |
| Sales call review | 1-2 hours | High | Identifying pre-purchase questions |
| Support ticket mining | 1-2 hours | High | Finding confusion points and tutorial needs |
| Competitor comment analysis | 1 hour | Medium | Discovering unmet content needs |
| Community forum research | 1-2 hours | Medium | Finding real questions and debates |
| Search console analysis | 30 min | Medium | Understanding what queries already reach you |
| Keyword research tools | 1-2 hours | Medium | Quantifying demand for topics |
Customer Language Extraction
The most valuable output of audience research is exact language:
| Customer Says | Content Opportunity |
|---|---|
| "I wish I knew..." | Educational content using their exact framing |
| "The hardest part is..." | Tutorial content addressing that specific difficulty |
| "I always forget to..." | Checklist or template content |
| "Everyone says to do X, but..." | Contrarian or nuanced content |
| "Is it worth it to..." | ROI analysis or comparison content |
| "What's the difference between..." | Comparison or explainer content |
---
Funnel Mapping
Content by Funnel Stage
| Stage | Reader Mindset | Content Goal | Content Types | Metrics |
|---|---|---|---|---|
| Awareness | "I have a problem" | Attract and educate | Blog posts, videos, infographics, social content | Traffic, impressions, shares |
| Consideration | "What are my options?" | Build trust and differentiate | Comparisons, case studies, guides, webinars | Time on page, email signups, return visits |
| Decision | "Is this the right solution?" | Convert | Product pages, demos, free trials, ROI calculators | Signups, trials, demo requests, purchases |
Funnel Mapping Template
For each content pillar, ensure coverage across all funnel stages:
## Pillar: [Topic]
### Awareness Topics (attract new visitors)
- [Topic targeting broad informational keyword]
- [Topic answering common beginner question]
- [Topic providing industry data or benchmark]
### Consideration Topics (build trust and preference)
- [Topic comparing approaches or solutions]
- [Topic showing how to evaluate options]
- [Case study demonstrating results]
### Decision Topics (drive conversion)
- [Product-specific tutorial or walkthrough]
- [ROI calculator or assessment tool]
- [FAQ addressing buying objections]---
Searchable vs. Shareable Matrix
Understanding the Distinction
| Dimension | Searchable Content | Shareable Content |
|---|---|---|
| Discovery | People search for it (keyword-driven) | People share it (social-driven) |
| Lifespan | Evergreen (months to years) | Timely (days to weeks) |
| Examples | "How to set up GA4 tracking" | "The state of marketing in 2026" |
| Traffic pattern | Steady, compounding | Spike, then decline |
| Optimization | SEO-first | Hook and emotion-first |
| Measurement | Organic traffic, rankings | Shares, engagement, referral traffic |
Strategy Balance
Most content strategies should be:
- 60-70% searchable (sustainable traffic engine)
- 20-30% shareable (brand awareness, audience growth)
- 10% promotional (product launches, offers)
Content Type Classification
| Content Type | Searchable | Shareable | Both |
|---|---|---|---|
| How-to guides | High | Low | — |
| Original research | Medium | High | Yes |
| Comparisons | High | Medium | Yes |
| Thought leadership | Low | High | — |
| Templates/tools | High | Medium | Yes |
| Case studies | Medium | Medium | Yes |
| Industry news takes | Low | High | — |
| Tutorials | High | Low | — |
| Frameworks/playbooks | Medium | High | Yes |
---
Competitive Content Analysis
Analysis Framework
For each top competitor:
## Competitor: [Name]
### Content Inventory
- Blog frequency: [Posts per week/month]
- Content types: [Formats they use]
- Average word count: [Length]
- Primary topics: [Their content pillars]
### Strengths
- [What they do well]
- [Topics where they dominate]
### Gaps
- [Topics they don't cover]
- [Angles they miss]
- [Outdated content that could be beaten]
### Opportunity
- [Where you can win with better content]
- [Underserved topics in their coverage]Competitive Gap Scoring
| Topic | Competitor A | Competitor B | Competitor C | Your Coverage | Gap Score |
|---|---|---|---|---|---|
| [Topic 1] | Strong | None | Weak | None | High opportunity |
| [Topic 2] | Strong | Strong | Strong | Weak | Improve existing |
| [Topic 3] | None | None | None | None | Blue ocean opportunity |
---
Best Practices
1. Strategy before production — A mediocre topic executed well is still a mediocre result. Spend time picking the right topics.
2. Customer research is non-negotiable — Content strategy built without customer input is guessing. Mine sales calls, support tickets, and customer language before planning.
3. Cover the full funnel — Most teams over-index on awareness content. Ensure each pillar has consideration and decision-stage content too.
4. Prioritize by business impact, not search volume — A 500-volume keyword with high purchase intent outperforms a 10,000-volume keyword with no commercial relevance.
5. Audit before you create — Most content libraries have underperforming content that could be improved for more impact than writing something new.
6. Interlink systematically — Topic clusters only work when content is connected through internal links. Build linking into the production process.
7. Refresh > Publish — Updating a post ranking #8 to reach #3 produces more traffic than a new post starting at position #50.
8. One pillar at a time — Build depth in one pillar before spreading across all of them. Authority comes from depth, not breadth.
9. Document your strategy — A strategy that only lives in someone's head is not a strategy. Write it down, share it, refer to it.
10. Measure quarterly — Content strategy is a 6-12 month investment. Monthly reviews for tactics, quarterly reviews for strategy adjustments.
---
Integration Points
- Content Production — Use for executing the strategy (writing, editing, publishing). Content Strategy decides what; Content Production does it.
- SEO Specialist — Use for technical SEO and keyword research to inform topic selection and prioritization.
- AI SEO — Use for optimizing content specifically for AI search citation alongside traditional SEO.
- Copywriting — Use when strategy identifies need for landing pages or conversion copy.
- Social Content — Use for distributing content across social platforms after publication.
- Marketing Context — Use as the foundation. Content strategy should align with ICP, positioning, and business goals.
- Campaign Analytics — Use to measure content performance and inform strategy adjustments.
---
Troubleshooting
| Problem | Likely Cause | Fix |
|---|---|---|
| Content published consistently but no organic traffic growth | Topics selected without keyword research or targeting zero-volume queries | Re-prioritize using search volume x business relevance x competitive gap scoring matrix |
| All content is awareness-stage, no leads generated | Over-indexing on top-of-funnel content without consideration/decision stage pieces | Audit funnel coverage per pillar; create 2-3 consideration and decision pieces per pillar |
| Topic cannibalization across content pieces | Multiple articles targeting the same keyword without differentiation | Map one primary keyword per page; consolidate competing pieces or differentiate angles |
| Content calendar frequently disrupted | Production timelines too tight or no buffer for reviews and revisions | Add 3-day buffer per piece; batch brief creation 4-6 pieces at a time |
| Competitor consistently outranks on shared topics | Competitor has stronger E-E-A-T signals or deeper content coverage | Analyze competitor content depth; add original data, expert quotes, and experience signals |
| Content audit reveals 70%+ of library is underperforming | Strategy was never defined — content was reactive, not planned | Start with 3 content pillars, build 10-15 cluster topics per pillar, then produce systematically |
---
Success Criteria
- Pillar coverage: 3-5 content pillars defined with 15+ cluster topics each, covering all three funnel stages
- Organic traffic contribution: Content driving 40%+ of total organic traffic within 12 months
- Topic cluster completeness: Each pillar has hub page + 10+ supporting pieces with bidirectional internal links
- Content freshness: 80%+ of library updated within the last 12 months; high-value content refreshed quarterly
- Funnel balance: Content split approximately 60% awareness, 25% consideration, 15% decision (or adjusted per business model)
- Conversion from content: Content-attributed leads growing 10%+ quarter-over-quarter
- Search visibility: 50%+ of target keywords ranking in top 20 within 6 months of strategy execution
---
Scope & Limitations
In scope:
- Content pillar definition and topic cluster planning
- Audience research methodology for content topics
- Keyword-informed topic prioritization
- Funnel mapping (awareness, consideration, decision)
- Content audit and remediation planning
- Competitive content gap analysis
- Editorial calendar structure and cadence planning
Out of scope:
- Content writing and production (use Content Production)
- SEO technical optimization (use SEO Specialist)
- Social media distribution strategy (use Social Content)
- Paid content promotion and advertising
- Content management system selection or setup
- Brand voice and messaging development
Known limitations:
- Content strategy requires 6-12 months to show compounding results — short-term ROI expectations are unrealistic
- AI Overviews reducing organic CTR means position 1 delivers fewer clicks than historical benchmarks
- Keyword volume data from tools is estimated and varies between providers
- Content audit accuracy depends on access to Google Search Console and analytics data
- Competitive content analysis is point-in-time; competitors update strategies continuously
---
Scripts
# Generate a content calendar plan from topic list
python scripts/content_calendar_planner.py --topics topics.csv --cadence weekly --json
# Analyze headlines for click-worthiness and SEO
python scripts/headline_analyzer.py --headlines headlines.txt --json
# Score content brief completeness
python scripts/content_brief_generator.py --keyword "cloud cost optimization" --json#!/usr/bin/env python3
"""
Content Brief Generator
Generates structured content briefs from keyword and topic inputs.
Includes target audience, search intent classification, recommended
structure, word count guidance, and internal linking suggestions.
Usage:
python content_brief_generator.py --keyword "cloud cost optimization"
python content_brief_generator.py --keyword "SEO audit checklist" --audience "marketing managers" --json
python content_brief_generator.py --keyword "best CRM for startups" --funnel consideration
"""
import argparse
import json
import re
import sys
INTENT_PATTERNS = {
"informational": r'\b(what is|how to|guide|tutorial|explain|definition|overview|learn)\b',
"commercial": r'\b(best|top|review|comparison|vs|alternative|software|tool|platform)\b',
"transactional": r'\b(buy|price|pricing|discount|free trial|download|sign up)\b',
"navigational": r'\b(login|docs|documentation|support|contact)\b',
}
CONTENT_STRUCTURES = {
"informational": {
"format": "Comprehensive Guide",
"word_count": "1,500-2,500",
"sections": [
"Definition and overview",
"Why it matters",
"Key components or steps",
"Best practices",
"Common mistakes",
"Tools and resources",
"FAQ",
],
},
"commercial": {
"format": "Comparison / Buyer's Guide",
"word_count": "2,000-3,000",
"sections": [
"Overview of the category",
"Key evaluation criteria",
"Detailed comparison (table format)",
"Pros and cons of each option",
"Best for specific use cases",
"Pricing comparison",
"Recommendation and verdict",
],
},
"transactional": {
"format": "Product-Focused Landing Content",
"word_count": "800-1,500",
"sections": [
"Problem statement",
"Solution overview",
"Key features and benefits",
"Social proof (testimonials, case studies)",
"Pricing and plans",
"Getting started steps",
"FAQ",
],
},
"navigational": {
"format": "Resource Page",
"word_count": "500-1,000",
"sections": [
"Direct answer or resource",
"Quick navigation links",
"Related resources",
],
},
}
FUNNEL_GUIDANCE = {
"awareness": {
"reader_mindset": "I have a problem or question but don't know solutions yet",
"content_goal": "Educate and attract — establish your expertise",
"cta": "Newsletter signup, related content, or resource download",
"tone": "Educational, helpful, non-promotional",
},
"consideration": {
"reader_mindset": "I know my options and I'm evaluating them",
"content_goal": "Build trust and differentiate your solution",
"cta": "Demo request, free trial, or case study download",
"tone": "Authoritative, balanced, evidence-based",
},
"decision": {
"reader_mindset": "I'm ready to choose — convince me this is the right one",
"content_goal": "Remove objections and drive conversion",
"cta": "Sign up, purchase, or contact sales",
"tone": "Confident, specific, proof-heavy",
},
}
def classify_intent(keyword):
"""Classify search intent."""
kw = keyword.lower()
scores = {}
for intent, pattern in INTENT_PATTERNS.items():
scores[intent] = len(re.findall(pattern, kw))
if max(scores.values()) == 0:
return "informational"
return max(scores, key=scores.get)
def estimate_word_count(intent, funnel):
"""Estimate target word count."""
base = CONTENT_STRUCTURES[intent]["word_count"]
if funnel == "awareness":
return base # Standard
elif funnel == "consideration":
return f"{base} (lean toward upper range for depth)"
else:
return "800-1,500 (concise, action-oriented)"
def generate_brief(keyword, audience=None, funnel=None):
"""Generate a complete content brief."""
intent = classify_intent(keyword)
if funnel is None:
funnel_map = {
"informational": "awareness",
"commercial": "consideration",
"transactional": "decision",
"navigational": "awareness",
}
funnel = funnel_map[intent]
structure = CONTENT_STRUCTURES[intent]
funnel_guide = FUNNEL_GUIDANCE.get(funnel, FUNNEL_GUIDANCE["awareness"])
brief = {
"keyword": keyword,
"intent": intent,
"funnel_stage": funnel,
"target": {
"primary_keyword": keyword,
"secondary_keywords": [
f"{keyword} guide",
f"{keyword} examples",
f"how to {keyword}",
f"best {keyword}",
f"{keyword} tips",
],
"search_intent": intent,
},
"audience": {
"profile": audience or "[Define: role, seniority, industry]",
"reader_mindset": funnel_guide["reader_mindset"],
"awareness_level": funnel.capitalize(),
},
"content_spec": {
"format": structure["format"],
"target_word_count": estimate_word_count(intent, funnel),
"tone": funnel_guide["tone"],
"cta": funnel_guide["cta"],
"content_goal": funnel_guide["content_goal"],
},
"structure": {
"h1": f"[Working Title] — include '{keyword}'",
"sections": structure["sections"],
},
"requirements": {
"internal_links": "3-5 links to related content",
"external_links": "2-3 links to authoritative sources",
"images": "1 per 500 words minimum",
"schema_markup": "Article schema at minimum; FAQPage if FAQ section included",
"meta_title": f"Under 60 chars, includes '{keyword}'",
"meta_description": "140-160 chars with keyword and clear value proposition",
},
"seo_checklist": [
f"Primary keyword in H1, first 100 words, and 2+ H2s",
"Keyword density 0.5-2.5%",
"All images have descriptive alt text",
"URL slug is short, keyword-first, no stop words",
"Heading hierarchy: H1 > H2 > H3 (no skips)",
],
"competitive_research": {
"instruction": f"Analyze top 5 ranking pages for '{keyword}'",
"what_to_record": [
"Content format and angle",
"Word count and depth",
"What they cover well",
"What they miss (your gap to fill)",
],
},
}
return brief
def main():
parser = argparse.ArgumentParser(description="Generate content briefs")
parser.add_argument("--keyword", required=True, help="Target keyword")
parser.add_argument("--audience", help="Target audience description")
parser.add_argument("--funnel", choices=["awareness", "consideration", "decision"])
parser.add_argument("--json", action="store_true")
args = parser.parse_args()
brief = generate_brief(args.keyword, args.audience, args.funnel)
if args.json:
print(json.dumps(brief, indent=2))
else:
print(f"\n{'='*60}")
print(f" CONTENT BRIEF: {args.keyword}")
print(f"{'='*60}")
print(f" Intent: {brief['intent']} | Funnel: {brief['funnel_stage']}")
print(f" Format: {brief['content_spec']['format']}")
print(f" Word Count: {brief['content_spec']['target_word_count']}")
print(f" Tone: {brief['content_spec']['tone']}")
print(f" CTA: {brief['content_spec']['cta']}")
print(f"\n Audience:")
print(f" Profile: {brief['audience']['profile']}")
print(f" Mindset: {brief['audience']['reader_mindset']}")
print(f"\n Recommended Structure:")
print(f" H1: {brief['structure']['h1']}")
for i, section in enumerate(brief["structure"]["sections"], 1):
print(f" H2 {i}: {section}")
print(f"\n SEO Checklist:")
for item in brief["seo_checklist"]:
print(f" [ ] {item}")
print(f"\n Secondary Keywords:")
for kw in brief["target"]["secondary_keywords"]:
print(f" - {kw}")
print()
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Content Calendar Planner
Generates a structured content calendar from a topic list, distributing
content across weeks by pillar, funnel stage, content type, and priority.
Supports weekly and bi-weekly cadences.
Usage:
python content_calendar_planner.py --topics topics.csv --cadence weekly
python content_calendar_planner.py --topics topics.csv --weeks 8 --json
python content_calendar_planner.py --topics topics.csv --cadence biweekly --start 2026-04-01
"""
import argparse
import csv
import json
import re
import sys
from datetime import date, timedelta
from pathlib import Path
def load_topics(filepath):
"""Load topics from CSV."""
topics = []
with open(filepath, 'r', encoding='utf-8') as f:
sample = f.read(1024)
f.seek(0)
if ',' in sample or '\t' in sample:
try:
dialect = csv.Sniffer().sniff(sample, delimiters=',\t')
except csv.Error:
dialect = csv.excel
reader = csv.DictReader(f, dialect=dialect)
for row in reader:
topic = {}
for key in ['topic', 'Topic', 'title', 'Title', 'keyword', 'Keyword']:
if key in row and row[key]:
topic["topic"] = row[key].strip()
break
if "topic" not in topic and row:
topic["topic"] = list(row.values())[0].strip()
for key in ['pillar', 'Pillar', 'category', 'Category']:
if key in row and row[key]:
topic["pillar"] = row[key].strip()
for key in ['funnel', 'Funnel', 'stage', 'Stage']:
if key in row and row[key]:
topic["funnel"] = row[key].strip().lower()
for key in ['type', 'Type', 'format', 'Format']:
if key in row and row[key]:
topic["content_type"] = row[key].strip()
for key in ['priority', 'Priority', 'score', 'Score']:
if key in row and row[key]:
try:
topic["priority"] = int(str(row[key]).strip())
except ValueError:
topic["priority"] = {"high": 3, "medium": 2, "low": 1}.get(
row[key].strip().lower(), 2
)
for key in ['volume', 'Volume']:
if key in row and row[key]:
try:
topic["volume"] = int(str(row[key]).replace(',', '').strip())
except ValueError:
pass
if topic.get("topic"):
topics.append(topic)
else:
for line in f:
t = line.strip()
if t:
topics.append({"topic": t})
return topics
def classify_topic(topic):
"""Auto-classify topic if metadata is missing."""
t = topic.get("topic", "").lower()
# Funnel stage
if "funnel" not in topic:
if any(w in t for w in ['what is', 'guide', 'introduction', 'basics', 'overview', 'how to']):
topic["funnel"] = "awareness"
elif any(w in t for w in ['vs', 'compare', 'best', 'review', 'alternative', 'case study']):
topic["funnel"] = "consideration"
elif any(w in t for w in ['pricing', 'demo', 'tutorial', 'setup', 'getting started']):
topic["funnel"] = "decision"
else:
topic["funnel"] = "awareness"
# Content type
if "content_type" not in topic:
if 'how to' in t or 'guide' in t or 'tutorial' in t:
topic["content_type"] = "guide"
elif 'vs' in t or 'compare' in t or 'best' in t:
topic["content_type"] = "comparison"
elif 'what is' in t:
topic["content_type"] = "explainer"
elif 'case study' in t or 'example' in t:
topic["content_type"] = "case_study"
else:
topic["content_type"] = "article"
# Priority
if "priority" not in topic:
topic["priority"] = 2
return topic
def plan_calendar(topics, cadence="weekly", weeks=8, start_date=None):
"""Plan content calendar distributing topics across weeks."""
if start_date is None:
start_date = date.today()
elif isinstance(start_date, str):
start_date = date.fromisoformat(start_date)
# Sort by priority (highest first)
topics.sort(key=lambda t: t.get("priority", 2), reverse=True)
# Determine posts per week
posts_per_week = 2 if cadence == "weekly" else 1
calendar = []
topic_index = 0
for week in range(weeks):
week_start = start_date + timedelta(weeks=week)
week_end = week_start + timedelta(days=6)
week_plan = {
"week": week + 1,
"start_date": week_start.isoformat(),
"end_date": week_end.isoformat(),
"content": [],
}
for slot in range(posts_per_week):
if topic_index < len(topics):
topic = topics[topic_index]
day_offset = 0 if slot == 0 else 3 # Mon and Thu
publish_date = week_start + timedelta(days=day_offset)
week_plan["content"].append({
"topic": topic["topic"],
"publish_date": publish_date.isoformat(),
"brief_due": (publish_date - timedelta(days=14)).isoformat(),
"draft_due": (publish_date - timedelta(days=9)).isoformat(),
"review_due": (publish_date - timedelta(days=5)).isoformat(),
"pillar": topic.get("pillar", "Unassigned"),
"funnel": topic.get("funnel", "awareness"),
"content_type": topic.get("content_type", "article"),
"priority": topic.get("priority", 2),
"volume": topic.get("volume"),
})
topic_index += 1
# Add refresh slot every 4th week
if (week + 1) % 4 == 0:
week_plan["content"].append({
"topic": "[REFRESH: Update highest-performing existing post]",
"publish_date": (week_start + timedelta(days=4)).isoformat(),
"content_type": "refresh",
"pillar": "Cross-pillar",
"funnel": "mixed",
"priority": 3,
})
calendar.append(week_plan)
return {
"cadence": cadence,
"total_weeks": weeks,
"total_pieces": topic_index,
"remaining_topics": len(topics) - topic_index,
"calendar": calendar,
}
def main():
parser = argparse.ArgumentParser(
description="Plan a content calendar from topic list"
)
parser.add_argument("--topics", required=True, help="CSV file with topics")
parser.add_argument("--cadence", choices=["weekly", "biweekly"], default="weekly")
parser.add_argument("--weeks", type=int, default=8, help="Number of weeks to plan (default: 8)")
parser.add_argument("--start", help="Start date (YYYY-MM-DD, default: today)")
parser.add_argument("--json", action="store_true")
args = parser.parse_args()
fp = Path(args.topics)
if not fp.exists():
print(f"Error: {fp} not found", file=sys.stderr)
sys.exit(1)
topics = load_topics(fp)
if not topics:
print("No topics found.", file=sys.stderr)
sys.exit(1)
topics = [classify_topic(t) for t in topics]
calendar = plan_calendar(topics, args.cadence, args.weeks, args.start)
if args.json:
print(json.dumps(calendar, indent=2))
else:
print(f"\n{'='*70}")
print(f" CONTENT CALENDAR — {calendar['total_weeks']} weeks, {calendar['total_pieces']} pieces")
print(f"{'='*70}")
print(f" Cadence: {args.cadence} | Remaining topics: {calendar['remaining_topics']}")
for week in calendar["calendar"]:
print(f"\n Week {week['week']} ({week['start_date']} to {week['end_date']}):")
for item in week["content"]:
funnel = item.get("funnel", "")[:5].upper()
ctype = item.get("content_type", "")[:10]
print(f" [{funnel}] {item['publish_date']} | {ctype:<10} | {item['topic'][:50]}")
# Summary by funnel
funnel_counts = {"awareness": 0, "consideration": 0, "decision": 0, "mixed": 0}
for week in calendar["calendar"]:
for item in week["content"]:
f = item.get("funnel", "awareness")
funnel_counts[f] = funnel_counts.get(f, 0) + 1
print(f"\n Funnel Distribution:")
for f, c in funnel_counts.items():
if c > 0:
print(f" {f.capitalize()}: {c} pieces")
print()
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Headline Analyzer
Analyzes headlines for SEO effectiveness, click-worthiness, emotional
impact, and readability. Scores headlines 0-100 and provides specific
improvement recommendations.
Usage:
python headline_analyzer.py --headline "10 Ways to Reduce Cloud Costs in 2026"
python headline_analyzer.py --headlines headlines.txt --json
python headline_analyzer.py --headline "My Title" --keyword "cloud costs"
"""
import argparse
import json
import re
import sys
from pathlib import Path
POWER_WORDS = {
"urgency": ["now", "today", "immediately", "hurry", "fast", "quick", "instant"],
"exclusivity": ["secret", "insider", "exclusive", "hidden", "little-known", "underground"],
"value": ["free", "save", "proven", "guaranteed", "essential", "ultimate", "complete"],
"curiosity": ["surprising", "unexpected", "strange", "weird", "shocking", "bizarre"],
"authority": ["expert", "research", "study", "data", "science", "official", "definitive"],
"emotion": ["amazing", "brilliant", "terrible", "devastating", "incredible", "powerful"],
}
WEAK_WORDS = [
"things", "stuff", "nice", "good", "bad", "great", "awesome", "cool",
"interesting", "important", "very", "really", "basically",
]
HEADLINE_FORMULAS = {
"number_list": r'^\d+\s+',
"how_to": r'^how\s+to\b',
"question": r'\?$',
"why": r'^why\s+',
"what": r'^what\s+',
"guide": r'\bguide\b',
"vs_comparison": r'\bvs\.?\b|\bversus\b',
"year": r'\b202[4-9]\b|\b203\d\b',
}
def analyze_headline(headline, keyword=None):
"""Analyze a single headline."""
hl = headline.strip()
hl_lower = hl.lower()
words = hl_lower.split()
word_count = len(words)
char_count = len(hl)
checks = {}
# Length (characters)
checks["char_length"] = {
"value": char_count,
"pass": 40 <= char_count <= 65,
"detail": f"{char_count} chars (target: 50-60 for SEO, max 65)",
}
# Word count
checks["word_count"] = {
"value": word_count,
"pass": 6 <= word_count <= 12,
"detail": f"{word_count} words (optimal: 6-12)",
}
# Number presence
has_number = bool(re.search(r'\d+', hl))
checks["has_number"] = {
"pass": has_number,
"detail": "Contains a number" if has_number else "No number — numbered headlines get 36% higher CTR",
}
# Power words
found_power = []
for category, pw_list in POWER_WORDS.items():
for word in pw_list:
if word in hl_lower:
found_power.append(f"{word} ({category})")
checks["power_words"] = {
"pass": len(found_power) >= 1,
"count": len(found_power),
"found": found_power[:5],
"detail": f"{len(found_power)} power words found" + (f": {', '.join(found_power[:3])}" if found_power else ""),
}
# Weak words
found_weak = [w for w in WEAK_WORDS if w in words]
checks["no_weak_words"] = {
"pass": len(found_weak) == 0,
"found": found_weak,
"detail": f"{len(found_weak)} weak words" + (f": {', '.join(found_weak)}" if found_weak else " — clean"),
}
# Formula match
matched_formula = None
for name, pattern in HEADLINE_FORMULAS.items():
if re.search(pattern, hl_lower):
matched_formula = name
break
checks["proven_formula"] = {
"pass": matched_formula is not None,
"formula": matched_formula,
"detail": f"Matches '{matched_formula}' formula" if matched_formula else "No proven headline formula detected",
}
# Front-loaded keyword
if keyword:
kw = keyword.lower()
in_headline = kw in hl_lower
front_loaded = hl_lower.startswith(kw) or hl_lower.find(kw) < char_count * 0.4
checks["keyword_placement"] = {
"pass": in_headline and front_loaded,
"in_headline": in_headline,
"front_loaded": front_loaded,
"detail": f"Keyword {'front-loaded' if front_loaded else 'present but not front-loaded' if in_headline else 'MISSING'}",
}
# Emotional score (based on power words + formula)
emotional_score = len(found_power) * 15 + (20 if has_number else 0) + (15 if matched_formula else 0)
emotional_score = min(emotional_score, 100)
checks["emotional_impact"] = {
"value": emotional_score,
"pass": emotional_score >= 30,
"detail": f"Emotional impact: {emotional_score}/100",
}
# Capitalization
is_title_case = hl[0].isupper()
checks["proper_case"] = {
"pass": is_title_case,
"detail": "Proper capitalization" if is_title_case else "Starts with lowercase",
}
# Calculate score
passed = sum(1 for c in checks.values() if c.get("pass", False))
score = round((passed / len(checks)) * 100, 1)
# Grade
grade = "A" if score >= 80 else "B" if score >= 65 else "C" if score >= 50 else "D" if score >= 35 else "F"
return {
"headline": hl,
"score": score,
"grade": grade,
"checks": checks,
}
def main():
parser = argparse.ArgumentParser(description="Analyze headlines for SEO and CTR")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--headline", help="Single headline to analyze")
group.add_argument("--headlines", help="File with headlines (one per line)")
parser.add_argument("--keyword", help="Target keyword for placement check")
parser.add_argument("--json", action="store_true")
args = parser.parse_args()
headlines = []
if args.headline:
headlines = [args.headline]
else:
fp = Path(args.headlines)
if not fp.exists():
print(f"Error: {fp} not found", file=sys.stderr)
sys.exit(1)
headlines = [l.strip() for l in fp.read_text().splitlines() if l.strip()]
results = [analyze_headline(h, args.keyword) for h in headlines]
if args.json:
print(json.dumps({"headlines": results, "total": len(results)}, indent=2))
else:
for r in results:
print(f"\n{'='*60}")
print(f" HEADLINE: {r['headline']}")
print(f" Score: {r['score']}/100 (Grade: {r['grade']})")
print(f"{'='*60}")
for key, check in r["checks"].items():
status = "PASS" if check.get("pass") else "FAIL"
print(f" [{status}] {key}: {check['detail']}")
if len(results) > 1:
avg = round(sum(r["score"] for r in results) / len(results), 1)
best = max(results, key=lambda r: r["score"])
print(f"\n Average score: {avg}/100")
print(f" Best: \"{best['headline']}\" ({best['score']}/100)")
print()
if __name__ == "__main__":
main()