
Content Production
- 100 installs
- 451 repo stars
- Updated July 21, 2026
- borghei/claude-skills
Content Production is a Claude skill that runs a full content pipeline from research and brief through drafting, SEO optimization, quality gates, and internal linking to a publish-ready piece.
About
Content Production is a full content pipeline that takes a topic from blank page to publish-ready piece. It covers competitive research, content briefs, outline-first drafting, SEO and readability optimization, editorial quality gates, internal linking, and repurposing. A marketer or writer uses it to produce blog posts, articles, and long-form content end-to-end or to set up content operations with an editorial calendar.
- End-to-end content pipeline: research, brief, draft, SEO/readability optimization, quality gates, internal linking
- Includes a content-brief framework and editorial-calendar management
- Repurposing system to multiply each piece across channels
Content Production by the numbers
- 100 all-time installs (skills.sh)
- Ranked #1,148 of 1,879 Marketing & SEO skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
content-production capabilities & compatibility
- Capabilities
- content writing · seo optimization · editorial calendar · content brief
- Use cases
- copywriting · seo · marketing
- Pricing
- Free
What content-production says it does
Full content production pipeline from blank page to publish-ready piece.
Collect 3-5 credible, citable sources before drafting:
Build an editorial calendar with 4-6 weeks of planned content
npx skills add https://github.com/borghei/claude-skills --skill content-productionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 100 |
|---|---|
| repo stars | ★ 451 |
| Last updated | July 21, 2026 |
| Repository | borghei/claude-skills ↗ |
What it does
Take a topic from research and brief through drafting, SEO optimization, quality gates, and publishing.
Who is it for?
Writers and marketers producing blog posts and long-form content end-to-end
Skip if: Short ad copy or product-page copywriting
When should I use this skill?
Writing a blog post, drafting an article, or setting up content operations and an editorial calendar
What you get
Produces a publish-ready, SEO-optimized piece that passes an editorial quality gate.
- content brief
- draft
- SEO-optimized piece
By the numbers
- Analyzes top 5 ranking pieces for a keyword
- Recommends 4-6 weeks of planned content
- Collects 3-5 credible citable sources before drafting
Files
Content Production
The execution engine for content — taking topics from blank page to published, optimized, and distributed.
---
Table of Contents
- Keywords
- Quick Start
- Core Workflows
- Content Brief Framework
- Drafting Methodology
- Optimization Pipeline
- Editorial Calendar Management
- Content Repurposing System
- Quality Gates
- Best Practices
- Integration Points
---
Keywords
content production, blog writing, article drafting, content pipeline, editorial workflow, content operations, content calendar, content brief, SEO content, content optimization, readability, internal linking, meta tags, content repurposing, content at scale, editorial calendar, content quality, publishing workflow, long-form content, content management
---
Quick Start
Write a Blog Post End-to-End
1. Research: analyze top 5 ranking pieces for the target keyword 2. Brief: define keyword targets, angle, audience, and H2 structure 3. Draft: write with outline-first approach, leading each section with its main point 4. Optimize: SEO pass, readability pass, structure audit, meta tags 5. Validate: run the quality gate checklist before publishing
Set Up Content Operations
1. Define content pillars aligned with business goals 2. Build an editorial calendar with 4-6 weeks of planned content 3. Establish the production workflow (brief > draft > edit > optimize > publish) 4. Set up repurposing workflow to multiply each piece across channels 5. Define quality gates that every piece must pass before publishing
---
Core Workflows
Workflow 1: Research and Brief
Step 1: Competitive Content Analysis
Before writing, understand what already ranks for your target keyword:
1. Identify the top 5-10 ranking pieces 2. Map their angles and formats:
| URL | Format | Word Count | Key Angle | What's Missing |
|---|---|---|---|---|
| [URL 1] | How-to guide | 2,400 | Step-by-step technical | No real-world examples |
| [URL 2] | Listicle | 1,800 | Tools comparison | Outdated, 2023 data |
| [URL 3] | Expert roundup | 3,100 | Multiple perspectives | No actionable framework |
3. Identify the content gap: what does nobody cover well? 4. Verify search intent:
| SERP Pattern | Intent | What to Write |
|---|---|---|
| "What is / How to" dominate | Informational | Comprehensive guide or explainer |
| Product pages, reviews | Commercial | Comparison or buyer's guide |
| Forum results (Reddit, Quora) | Discovery | Opinionated piece with real perspective |
| News, recent articles | Trending | Timely take with unique angle |
Step 2: Source Gathering
Collect 3-5 credible, citable sources before drafting:
- Original research (studies, surveys, published reports)
- Official documentation or industry standards
- Expert quotes with full attribution
- Data with specific numbers (not vague claims)
Rule: If you cannot cite a specific number, do not make a vague claim.
Step 3: Produce the Content Brief
## Content Brief
### Target
- Primary keyword: [keyword] (volume: [X], difficulty: [X])
- Secondary keywords: [keyword 1], [keyword 2], [keyword 3]
- Search intent: [Informational / Commercial / Transactional]
### Audience
- Reader profile: [Who they are and what they know]
- Job-to-be-done: [What problem they are solving right now]
- Awareness level: [Unaware / Problem-aware / Solution-aware]
### Angle
- Unique perspective: [What makes this piece different]
- Core argument: [The single thesis of this piece]
- Key claims to prove: [3-5 specific claims with supporting evidence]
### Structure
- H1: [Working title]
- H2: [Section 1]
- H2: [Section 2]
- H2: [Section 3]
- H2: [Section 4]
- H2: [Conclusion / Next Steps]
### Requirements
- Target word count: [X]
- Internal links to include: [List existing pages to link to]
- Competitive pieces to beat: [Top 3 URLs to outperform]
- CTA: [What action should the reader take]Workflow 2: Drafting
Step 1: Build the Outline
Before writing prose, create the header skeleton:
- H1 that includes the keyword and creates curiosity
- 4-7 H2 sections in logical progression
- H3s only when a section genuinely needs subdivision
- Conclusion with CTA
Step 2: Write the Introduction
The intro has one job: make the reader believe this piece answers their question.
Formula: 1. Name the problem or situation the reader is in (1 sentence) 2. Name what this piece does about it (1 sentence) 3. Establish credibility if relevant (1 sentence, optional)
What to avoid:
- "In today's digital landscape..." (everyone does this)
- Starting with a question unless it is genuinely sharp
- Three sentences of context before reaching the point
Step 3: Write Section by Section
For each H2: 1. State the main point in the first sentence 2. Prove it with an example, statistic, or comparison 3. Provide one actionable takeaway before moving to the next section 4. Use transitional phrases to connect sections naturally
Step 4: Write the Conclusion
Three elements: 1. Summary of core argument (1-2 sentences, not a repeat of the intro) 2. The single most important next step 3. CTA aligned with the content goal
Workflow 3: Optimization Pipeline
Run these passes in order on every draft:
Pass 1: SEO Optimization
- [ ] Title tag: contains primary keyword, under 60 characters, curiosity-driving
- [ ] H1: different from title tag, keyword-rich, reads naturally
- [ ] H2s: at least 2-3 contain secondary keywords or related phrases
- [ ] First paragraph: primary keyword appears in first 100 words
- [ ] Keyword density: primary keyword appears naturally 3-5 times (not stuffed)
- [ ] Image alt text: descriptive, includes keyword where natural
- [ ] URL slug: short, keyword-first, no stop words
- [ ] Internal links: 2-4 minimum, linking to and from related content
- [ ] External links: 1-3 to authoritative sources
Pass 2: Readability
- [ ] Average sentence length: 15-20 words with variation
- [ ] No paragraph exceeds 4 sentences
- [ ] Active voice used in 80%+ of sentences
- [ ] Jargon explained on first use for non-expert audiences
- [ ] At least one visual element (image, table, diagram) per 500 words
- [ ] Subheadings visible every 200-300 words
- [ ] Readability score target: 60-70 (Flesch-Kincaid)
Pass 3: Structure Audit
- [ ] Intro delivers on the headline's promise
- [ ] Every H2 earns its place (cut sections that add no value)
- [ ] At least 2 concrete examples or illustrations
- [ ] Conclusion feels earned, not padded
- [ ] Content flow follows logical progression
- [ ] No orphan sections that could be merged
Pass 4: Meta Content
- [ ] Meta description: 150-160 characters, includes keyword, ends with hook
- [ ] OG title: optimized for social sharing (can differ from meta title)
- [ ] OG description: optimized for social click-through
- [ ] Canonical URL: set correctly
- [ ] Schema markup: Article schema at minimum
---
Editorial Calendar Management
Calendar Structure
| Week | Monday | Wednesday | Friday |
|---|---|---|---|
| 1 | [Pillar topic A] | [Supporting topic B] | [Social repurpose] |
| 2 | [Pillar topic C] | [Guest/collab piece] | [Update old post] |
| 3 | [Pillar topic A] | [Supporting topic D] | [Social repurpose] |
| 4 | [Data/research piece] | [Supporting topic E] | [Roundup/compilation] |
Content Types by Frequency
| Type | Frequency | Purpose |
|---|---|---|
| Pillar posts (2,000+ words) | 2x/month | Authority building, SEO ranking |
| Supporting posts (800-1,500 words) | 2-4x/month | Topic cluster depth, internal linking |
| Update/refresh existing posts | 2x/month | Maintain ranking, improve performance |
| Data/research pieces | 1x/month | Backlink attraction, original insights |
| Guest/collaboration | 1x/month | Audience expansion, backlinks |
Production Timeline
| Stage | Owner | Duration | Deadline Relative to Publish |
|---|---|---|---|
| Brief creation | Strategist | 1 day | Publish - 14 days |
| Research and outline | Writer | 2 days | Publish - 12 days |
| First draft | Writer | 3 days | Publish - 9 days |
| Editorial review | Editor | 2 days | Publish - 7 days |
| Revisions | Writer | 1 day | Publish - 5 days |
| SEO optimization | SEO lead | 1 day | Publish - 4 days |
| Final review | Editor | 1 day | Publish - 3 days |
| Staging and QA | Operations | 1 day | Publish - 2 days |
| Publish | Operations | — | Publish day |
---
Content Repurposing System
One Piece, Many Formats
Every pillar content piece should produce 5-8 derivative pieces:
| Source | Derivative | Platform | Effort |
|---|---|---|---|
| Blog post | Key insight thread | Twitter/X | Low |
| Blog post | Carousel of main points | Medium | |
| Blog post | Short-form video summary | TikTok/Reels | Medium |
| Blog post | Newsletter section | Low | |
| Blog post | Slide deck | SlideShare/LinkedIn | Medium |
| Blog post | Podcast discussion topic | Podcast | Low |
| Blog post | Infographic | Pinterest/Blog | High |
| Blog post | FAQ page content | Website | Low |
Repurposing Workflow
1. Publish the pillar piece — Let it index and get initial traction 2. Extract 3-5 key insights — Each insight becomes a standalone social piece 3. Adapt format per platform — Not copy-paste; reformat for each platform's norms 4. Schedule distribution — Spread across 1-2 weeks after publication 5. Cross-link everything — Social pieces link back to the pillar content 6. Track performance — Identify which derivative formats drive the most traffic back
Evergreen Refresh Cycle
For high-performing content:
- Review quarterly for accuracy and relevance
- Update statistics and data points annually
- Refresh publish date after significant updates
- Re-promote refreshed content through social and email
- Add new internal links as related content is published
---
Quality Gates
Pre-Publish Checklist
Every piece must pass these gates before publishing:
Content Quality:
- [ ] Core thesis is stated clearly in the first 200 words
- [ ] Every factual claim has a source or is labeled as opinion
- [ ] At least one image, table, or visual element
- [ ] Introduction does not start with a cliche
- [ ] Word count is within 10% of target
- [ ] No AI-sounding patterns (run Content Humanizer audit if uncertain)
SEO Quality:
- [ ] Primary keyword in title, H1, first 100 words, and 2+ H2s
- [ ] Meta title under 60 characters with keyword
- [ ] Meta description 150-160 characters with keyword and hook
- [ ] All images have descriptive alt text
- [ ] 2-4 internal links present and functional
- [ ] URL slug is clean and keyword-first
Technical Quality:
- [ ] All links verified and functional
- [ ] Images optimized for web (compressed, appropriate dimensions)
- [ ] Mobile rendering verified
- [ ] Schema markup implemented
- [ ] Canonical URL set
- [ ] OG tags configured for social sharing
Editorial Quality:
- [ ] Spelling and grammar checked
- [ ] Consistent formatting throughout
- [ ] Heading hierarchy maintained (H1 > H2 > H3, no skips)
- [ ] Consistent voice and tone from start to finish
- [ ] CTA present and aligned with content goal
---
Best Practices
1. Brief first, always — Never start writing without a brief. Even a 5-minute brief prevents 2 hours of rewriting.
2. One angle per piece — A piece that tries to be a how-to, a comparison, and an opinion piece fails at all three. Pick one angle.
3. Front-load value — The reader should get something useful in the first 300 words. Do not make them scroll to find the point.
4. Optimize existing before creating new — Refreshing a post ranked #8 to #3 produces more traffic than a new post starting at #50.
5. Internal linking is not optional — Every new piece should link to 2-4 existing pieces, and 2-4 existing pieces should be updated to link to the new piece.
6. Kill your darlings — If a section does not serve the reader or the keyword strategy, cut it. Length without value hurts ranking.
7. Batch production — Write briefs for 4-6 pieces at once, then draft in batches. Context-switching between briefing and writing reduces quality.
8. Measure what matters — Track organic traffic, time on page, and conversion rate. Vanity metrics (word count, publishing frequency alone) are misleading.
9. Repurpose systematically — Every pillar piece should produce 5+ derivative pieces. Build repurposing into the production workflow, not as an afterthought.
10. Maintain a content debt backlog — Track content that needs updating, broken links, outdated statistics, and missing internal links. Address content debt regularly.
---
Integration Points
- Content Strategy — Use for deciding what to write (topics, calendar, pillar structure). Content Production handles the execution.
- Content Humanizer — Use after drafting when the piece sounds robotic. Run before the SEO optimization pass.
- AI SEO — Use for optimizing specifically for AI search citation in addition to traditional SEO.
- Copywriting — Use for landing pages, CTAs, and conversion copy. Content Production handles long-form content.
- SEO Specialist — Use for technical SEO audits across the content library. Content Production handles per-piece optimization.
- Social Content — Use for distributing and repurposing content across social platforms.
- Copy Editing — Use for the editorial review pass within the production pipeline.
---
Troubleshooting
| Problem | Likely Cause | Fix |
|---|---|---|
| Content consistently fails the quality gate checklist | Briefs are incomplete or writers do not have enough context | Invest more time in brief creation — include target keyword, angle, audience, H2 structure, and competitive gaps |
| Published content ranks #15-30 but does not break into top 10 | Content depth insufficient or missing E-E-A-T signals compared to page 1 results | Add original data, expert quotes, experience signals; increase word count to match or exceed top-ranking competitors |
| Content sounds robotic after AI-assisted drafting | AI patterns not caught during editing passes | Run Content Humanizer audit before SEO optimization pass; check for filler words, hedging, and structural uniformity |
| Internal linking neglected on new posts | Not built into production workflow | Add "update 2-4 existing posts to link to new content" as a required step in every production checklist |
| Repurposing never happens despite planning | Repurposing treated as afterthought instead of workflow step | Build repurposing into the production timeline — schedule derivative content in the same editorial calendar |
| Production bottleneck at editorial review stage | Single editor reviewing all content; no SLA on review turnaround | Set 48-hour review SLA; create self-service checklists writers can run before submitting for review |
| Content outdated within 6 months of publishing | No refresh cycle established | Schedule quarterly reviews for all content ranking top 20; update stats, links, and publish dates |
---
Success Criteria
- Production velocity: Maintaining planned cadence (2-4 pieces/week) with less than 10% schedule slippage
- Quality gate pass rate: 90%+ of drafts passing the pre-publish quality gate checklist on first or second review
- SEO optimization score: Average score of 75+ on the four-pass optimization pipeline (SEO, readability, structure, meta)
- Readability score: Flesch Reading Ease of 60-70 across all published content (appropriate for web audiences)
- Internal link coverage: Every new piece includes 3-5 internal links, and 3+ existing pieces updated to link back
- Repurposing multiplier: Each pillar piece produces 5+ derivative pieces across social, email, and other channels
- Content performance: 60%+ of content published in last 6 months generating organic impressions within 90 days
---
Scope & Limitations
In scope:
- Full content production pipeline from brief to publish
- Competitive research and source gathering for content briefs
- Drafting methodology (outline-first, section-by-section)
- Four-pass optimization pipeline (SEO, readability, structure, meta)
- Editorial calendar management and production timelines
- Content repurposing system across channels
- Quality gates and pre-publish checklists
Out of scope:
- Content strategy and topic selection (use Content Strategy)
- AI content humanization (use Content Humanizer)
- Detailed copy editing (use Copy Editing)
- Technical SEO audits (use SEO Specialist)
- Social media platform management (use Social Content)
- Design and visual content creation
Known limitations:
- 60% of marketing teams now use AI in content workflows (2026 data) — quality control processes must account for AI-assisted drafting
- Production timelines assume dedicated writer and editor roles; solo operators need adjusted timelines
- Content performance measurement requires Google Search Console and analytics access
- Repurposing effectiveness varies by platform — not all derivative formats will perform equally
---
Scripts
# Score content readability with detailed metrics
python scripts/readability_scorer.py article.md --json
# Generate a content brief from keyword research
python scripts/content_brief_generator.py --keyword "cloud cost optimization" --json
# Analyze headline options for a new piece
python scripts/headline_analyzer.py --headlines headlines.txt --json#!/usr/bin/env python3
"""
Content Brief Generator for Production
Generates production-ready content briefs with keyword targets, audience
definition, recommended structure, competitive research prompts, and
SEO requirements. Designed for the content production pipeline.
Usage:
python content_brief_generator.py --keyword "cloud cost optimization"
python content_brief_generator.py --keyword "SEO audit" --audience "CTOs" --json
python content_brief_generator.py --keyword "best CRM" --format comparison
"""
import argparse
import json
import re
import sys
def classify_intent(keyword):
"""Classify search intent."""
kw = keyword.lower()
if re.search(r'\b(what is|how to|guide|tutorial|explain|learn|overview)\b', kw):
return "informational"
if re.search(r'\b(best|top|review|vs|compare|alternative|tool|software)\b', kw):
return "commercial"
if re.search(r'\b(buy|price|pricing|discount|free trial|sign up|download)\b', kw):
return "transactional"
return "informational"
FORMAT_SPECS = {
"guide": {
"name": "Comprehensive Guide",
"word_count": "1,800-2,500",
"h2_count": "5-7",
"sections": [
"What is [topic] (definition)",
"Why [topic] matters",
"How to [topic] (step-by-step)",
"Best practices",
"Common mistakes to avoid",
"Tools and resources",
"FAQ",
],
},
"comparison": {
"name": "Comparison / Buyer's Guide",
"word_count": "2,000-3,000",
"h2_count": "6-8",
"sections": [
"Overview of options",
"Evaluation criteria",
"Detailed comparison (table)",
"Pros and cons per option",
"Best for [use case 1]",
"Best for [use case 2]",
"Pricing comparison",
"Our recommendation",
],
},
"listicle": {
"name": "List Article",
"word_count": "1,500-2,000",
"h2_count": "7-12",
"sections": [
"Introduction (why this list matters)",
"[Item 1] — description + use case",
"[Item 2] — description + use case",
"[Item N] — description + use case",
"How to choose the right one",
"FAQ",
],
},
"how_to": {
"name": "How-To Tutorial",
"word_count": "1,200-2,000",
"h2_count": "5-8",
"sections": [
"What you need before starting",
"Step 1: [action]",
"Step 2: [action]",
"Step N: [action]",
"Common issues and fixes",
"Next steps",
],
},
}
def generate_brief(keyword, audience=None, content_format=None):
"""Generate a production-ready content brief."""
intent = classify_intent(keyword)
if content_format is None:
format_map = {
"informational": "guide",
"commercial": "comparison",
"transactional": "how_to",
}
content_format = format_map.get(intent, "guide")
spec = FORMAT_SPECS.get(content_format, FORMAT_SPECS["guide"])
return {
"brief_type": "production",
"keyword": keyword,
"intent": intent,
"audience": audience or "[Define target reader: role, seniority, industry]",
"format": spec["name"],
"target_word_count": spec["word_count"],
"structure": {
"h1": f"[Compelling title with '{keyword}' front-loaded, under 60 chars]",
"target_h2_count": spec["h2_count"],
"sections": spec["sections"],
},
"seo_requirements": {
"primary_keyword": keyword,
"secondary_keywords": [
f"{keyword} guide",
f"{keyword} best practices",
f"how to {keyword}",
f"{keyword} examples",
f"{keyword} {str(2026)}",
],
"keyword_in_h1": True,
"keyword_in_first_100_words": True,
"keyword_in_2_plus_h2s": True,
"meta_title": f"Under 60 chars with '{keyword}'",
"meta_description": "140-160 chars with keyword + value prop + hook",
"url_slug": f"/{keyword.lower().replace(' ', '-')}",
"internal_links": "3-5 to related content",
"external_links": "2-3 to authoritative sources",
"schema": "Article (minimum), FAQPage if FAQ included",
},
"quality_requirements": {
"readability": "Flesch Reading Ease 60-70",
"max_paragraph_length": "4 sentences",
"images": "1 per 500 words",
"active_voice": "80%+ of sentences",
"no_ai_filler_words": True,
"sources_required": "3-5 credible, citable sources",
},
"competitive_research": {
"task": f"Analyze top 5 ranking pages for '{keyword}'",
"record_per_competitor": [
"URL, format, word count",
"Key angle or unique perspective",
"What they cover well",
"What they miss (your opportunity)",
],
},
"production_timeline": {
"brief_review": "Day 0",
"research_and_outline": "Days 1-2",
"first_draft": "Days 3-5",
"editorial_review": "Days 6-7",
"revisions": "Day 8",
"seo_optimization": "Day 9",
"final_review_and_staging": "Day 10",
"publish": "Day 11",
},
}
def main():
parser = argparse.ArgumentParser(description="Generate production content briefs")
parser.add_argument("--keyword", required=True)
parser.add_argument("--audience", help="Target audience")
parser.add_argument("--format", choices=list(FORMAT_SPECS.keys()), help="Content format")
parser.add_argument("--json", action="store_true")
args = parser.parse_args()
brief = generate_brief(args.keyword, args.audience, args.format)
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" Format: {brief['format']} | Intent: {brief['intent']}")
print(f" Word count: {brief['target_word_count']}")
print(f" Audience: {brief['audience']}")
print(f"\n Structure ({brief['structure']['target_h2_count']} H2s):")
print(f" H1: {brief['structure']['h1']}")
for i, s in enumerate(brief["structure"]["sections"], 1):
print(f" H2.{i}: {s}")
print(f"\n SEO Requirements:")
print(f" - Meta title: {brief['seo_requirements']['meta_title']}")
print(f" - URL: {brief['seo_requirements']['url_slug']}")
print(f" - Internal links: {brief['seo_requirements']['internal_links']}")
print(f" - Schema: {brief['seo_requirements']['schema']}")
print(f"\n Timeline:")
for stage, day in brief["production_timeline"].items():
print(f" - {stage.replace('_', ' ').title()}: {day}")
print()
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Production Headline Analyzer
Analyzes and scores headline options for content production. Checks
SEO length compliance, power word usage, number presence, emotional
impact, and formula matching. Recommends the best headline from a set.
Usage:
python headline_analyzer.py --headline "10 Ways to Cut Cloud Costs in 2026"
python headline_analyzer.py --headlines headlines.txt --json
python headline_analyzer.py --headline "Title Here" --keyword "cloud costs"
"""
import argparse
import json
import re
import sys
from pathlib import Path
POWER_WORDS = [
'proven', 'ultimate', 'complete', 'essential', 'free', 'new', 'easy',
'fast', 'quick', 'simple', 'guaranteed', 'powerful', 'secret', 'insider',
'surprising', 'unexpected', 'amazing', 'incredible', 'best', 'top',
'guide', 'step', 'hack', 'strategy', 'framework', 'checklist', 'template',
]
WEAK_WORDS = [
'things', 'stuff', 'nice', 'good', 'bad', 'great', 'awesome', 'interesting',
'important', 'very', 'really', 'basically', 'actually',
]
FORMULAS = {
"number_list": (r'^\d+\s+', "Numbered list — high CTR format"),
"how_to": (r'^how\s+to\b', "How-to — matches informational intent"),
"question": (r'\?$', "Question — triggers curiosity"),
"year": (r'\b202[4-9]\b', "Year tag — signals freshness"),
"guide": (r'\bguide\b', "Guide label — promises comprehensiveness"),
"comparison": (r'\bvs\.?\b|\bversus\b', "Comparison — matches commercial intent"),
"colon_format": (r':', "Colon format — establishes topic then value"),
}
def score_headline(headline, keyword=None):
"""Score a headline 0-100."""
hl = headline.strip()
hl_lower = hl.lower()
words = hl_lower.split()
chars = len(hl)
score = 0
checks = []
# Character length (max 30 points)
if 45 <= chars <= 60:
score += 30
checks.append(("PASS", f"Length: {chars} chars (optimal)"))
elif 35 <= chars <= 65:
score += 20
checks.append(("PASS", f"Length: {chars} chars (acceptable)"))
else:
score += 5
checks.append(("FAIL", f"Length: {chars} chars (target 50-60)"))
# Number (15 points)
if re.search(r'\d+', hl):
score += 15
checks.append(("PASS", "Contains number"))
else:
checks.append(("FAIL", "No number — adds 36% CTR lift"))
# Power words (15 points)
found = [w for w in POWER_WORDS if w in hl_lower]
if found:
score += 15
checks.append(("PASS", f"Power words: {', '.join(found[:3])}"))
else:
checks.append(("FAIL", "No power words detected"))
# No weak words (10 points)
weak = [w for w in WEAK_WORDS if w in words]
if not weak:
score += 10
checks.append(("PASS", "No weak words"))
else:
checks.append(("FAIL", f"Weak words: {', '.join(weak)}"))
# Formula match (15 points)
matched = None
for name, (pattern, desc) in FORMULAS.items():
if re.search(pattern, hl_lower):
matched = desc
break
if matched:
score += 15
checks.append(("PASS", f"Formula: {matched}"))
else:
checks.append(("FAIL", "No proven formula pattern"))
# Keyword (15 points)
if keyword:
kw = keyword.lower()
if kw in hl_lower:
pos = hl_lower.find(kw)
if pos < chars * 0.4:
score += 15
checks.append(("PASS", "Keyword front-loaded"))
else:
score += 10
checks.append(("PASS", "Keyword present but not front-loaded"))
else:
checks.append(("FAIL", f"Keyword '{keyword}' missing"))
else:
score += 8 # Neutral if no keyword given
return {
"headline": hl,
"score": min(score, 100),
"grade": "A" if score >= 80 else "B" if score >= 65 else "C" if score >= 50 else "D" if score >= 35 else "F",
"char_count": chars,
"word_count": len(words),
"checks": checks,
}
def main():
parser = argparse.ArgumentParser(description="Analyze headlines for production")
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument("--headline", help="Single headline")
group.add_argument("--headlines", help="File with headlines")
parser.add_argument("--keyword")
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 = [score_headline(h, args.keyword) for h in headlines]
if args.json:
print(json.dumps({"headlines": results}, indent=2))
else:
for r in results:
print(f"\n {'='*55}")
print(f" {r['headline']}")
print(f" Score: {r['score']}/100 (Grade: {r['grade']}) | {r['char_count']} chars")
for status, detail in r["checks"]:
print(f" [{status}] {detail}")
if len(results) > 1:
best = max(results, key=lambda r: r["score"])
print(f"\n RECOMMENDED: \"{best['headline']}\" ({best['score']}/100)")
print()
if __name__ == "__main__":
main()
#!/usr/bin/env python3
"""
Content Readability Scorer
Comprehensive readability analysis including Flesch Reading Ease,
Flesch-Kincaid Grade Level, sentence length analysis, paragraph
analysis, passive voice detection, and web readability scoring.
Usage:
python readability_scorer.py article.md
python readability_scorer.py article.md --json
python readability_scorer.py article.md --verbose
"""
import argparse
import json
import math
import re
import sys
from pathlib import Path
def strip_markdown(text):
"""Remove markdown formatting for analysis."""
text = re.sub(r'^#{1,6}\s+', '', text, flags=re.MULTILINE)
text = re.sub(r'\*\*([^*]+)\*\*', r'\1', text)
text = re.sub(r'\*([^*]+)\*', r'\1', text)
text = re.sub(r'`[^`]+`', '', text)
text = re.sub(r'```.*?```', '', text, flags=re.DOTALL)
text = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', text)
text = re.sub(r'^\s*[\-\*]\s+', '', text, flags=re.MULTILINE)
text = re.sub(r'^\s*\d+\.\s+', '', text, flags=re.MULTILINE)
text = re.sub(r'^\|.*\|$', '', text, flags=re.MULTILINE)
text = re.sub(r'^---+$', '', text, flags=re.MULTILINE)
text = re.sub(r'\n{3,}', '\n\n', text)
return text.strip()
def count_syllables(word):
"""Estimate syllable count."""
word = word.lower().strip('.,!?;:()[]{}"\'-')
if len(word) <= 3:
return 1
count = 0
vowels = 'aeiouy'
prev = False
for c in word:
v = c in vowels
if v and not prev:
count += 1
prev = v
if word.endswith('e') and count > 1:
count -= 1
if word.endswith('le') and len(word) > 2 and word[-3] not in vowels:
count += 1
return max(count, 1)
def get_sentences(text):
"""Split text into sentences."""
sentences = re.split(r'(?<=[.!?])\s+(?=[A-Z])', text)
return [s.strip() for s in sentences if s.strip() and len(s.split()) >= 3]
def get_paragraphs(text):
"""Split text into paragraphs."""
return [p.strip() for p in re.split(r'\n\s*\n', text) if p.strip()]
def flesch_reading_ease(words, sentences, syllables):
"""Calculate Flesch Reading Ease score."""
if words == 0 or sentences == 0:
return 0
return round(206.835 - 1.015 * (words / sentences) - 84.6 * (syllables / words), 1)
def flesch_kincaid_grade(words, sentences, syllables):
"""Calculate Flesch-Kincaid Grade Level."""
if words == 0 or sentences == 0:
return 0
return round(0.39 * (words / sentences) + 11.8 * (syllables / words) - 15.59, 1)
def detect_passive_voice(text):
"""Detect passive voice constructions."""
passive_patterns = [
r'\b(is|are|was|were|been|being)\s+\w+ed\b',
r'\b(is|are|was|were|been|being)\s+\w+en\b',
r'\b(got|get|gets|getting)\s+\w+ed\b',
]
count = 0
examples = []
sentences = get_sentences(text)
for sent in sentences:
for pattern in passive_patterns:
if re.search(pattern, sent, re.IGNORECASE):
count += 1
if len(examples) < 5:
examples.append(sent[:80] + "..." if len(sent) > 80 else sent)
break
return count, examples
def analyze_sentence_variety(sentences):
"""Analyze sentence length variety."""
if not sentences:
return {}
lengths = [len(s.split()) for s in sentences]
avg = sum(lengths) / len(lengths)
variance = sum((l - avg) ** 2 for l in lengths) / len(lengths)
std_dev = math.sqrt(variance)
# Classify
short = sum(1 for l in lengths if l < 10)
medium = sum(1 for l in lengths if 10 <= l <= 20)
long_s = sum(1 for l in lengths if l > 20)
very_long = sum(1 for l in lengths if l > 30)
return {
"average_length": round(avg, 1),
"std_deviation": round(std_dev, 1),
"shortest": min(lengths),
"longest": max(lengths),
"distribution": {
"short_under_10": short,
"medium_10_to_20": medium,
"long_over_20": long_s,
"very_long_over_30": very_long,
},
"variety_rating": "Good" if std_dev > 5 else "Moderate" if std_dev > 3 else "Low (monotonous)",
}
def score_web_readability(flesch, avg_sentence, passive_rate, para_analysis):
"""Calculate web-specific readability score 0-100."""
score = 0
# Flesch Reading Ease (target 60-70 for web)
if 55 <= flesch <= 75:
score += 30
elif 45 <= flesch <= 80:
score += 20
else:
score += 10
# Sentence length (target 15-20 avg)
if 12 <= avg_sentence <= 22:
score += 25
elif 10 <= avg_sentence <= 25:
score += 15
else:
score += 5
# Passive voice (target < 10%)
if passive_rate < 10:
score += 20
elif passive_rate < 20:
score += 10
else:
score += 5
# Paragraph length
if para_analysis.get("avg_sentences", 0) <= 4:
score += 25
elif para_analysis.get("avg_sentences", 0) <= 6:
score += 15
else:
score += 5
return min(score, 100)
def main():
parser = argparse.ArgumentParser(description="Score content readability")
parser.add_argument("file", help="Content file to analyze")
parser.add_argument("--json", action="store_true")
parser.add_argument("--verbose", action="store_true")
args = parser.parse_args()
fp = Path(args.file)
if not fp.exists():
print(f"Error: {fp} not found", file=sys.stderr)
sys.exit(1)
raw_text = fp.read_text(encoding="utf-8", errors="replace")
text = strip_markdown(raw_text)
words_list = text.split()
word_count = len(words_list)
sentences = get_sentences(text)
sentence_count = len(sentences)
syllable_count = sum(count_syllables(w) for w in words_list)
paragraphs = get_paragraphs(text)
# Core scores
fre = flesch_reading_ease(word_count, sentence_count, syllable_count)
fkg = flesch_kincaid_grade(word_count, sentence_count, syllable_count)
# Sentence analysis
variety = analyze_sentence_variety(sentences)
avg_sentence_len = variety.get("average_length", 0)
# Passive voice
passive_count, passive_examples = detect_passive_voice(text)
passive_rate = round(passive_count / max(sentence_count, 1) * 100, 1)
# Paragraph analysis
para_sentence_counts = [len(get_sentences(p)) for p in paragraphs]
para_analysis = {
"count": len(paragraphs),
"avg_sentences": round(sum(para_sentence_counts) / max(len(para_sentence_counts), 1), 1),
"long_paragraphs": sum(1 for c in para_sentence_counts if c > 5),
}
# Web readability score
web_score = score_web_readability(fre, avg_sentence_len, passive_rate, para_analysis)
# Reading level label
if fre >= 80:
level = "Easy (6th grade)"
elif fre >= 60:
level = "Standard (8th-9th grade)"
elif fre >= 40:
level = "Difficult (College level)"
else:
level = "Very Difficult (Graduate level)"
result = {
"file": str(fp),
"word_count": word_count,
"sentence_count": sentence_count,
"paragraph_count": len(paragraphs),
"flesch_reading_ease": fre,
"flesch_kincaid_grade": fkg,
"reading_level": level,
"web_readability_score": web_score,
"sentence_variety": variety,
"passive_voice": {
"count": passive_count,
"rate": passive_rate,
"examples": passive_examples,
},
"paragraph_analysis": para_analysis,
}
if args.json:
print(json.dumps(result, indent=2))
else:
grade = "A" if web_score >= 80 else "B" if web_score >= 65 else "C" if web_score >= 50 else "D"
print(f"\n{'='*55}")
print(f" READABILITY SCORE: {web_score}/100 (Grade: {grade})")
print(f"{'='*55}")
print(f" File: {fp}")
print(f" Words: {word_count} | Sentences: {sentence_count} | Paragraphs: {len(paragraphs)}")
print(f"\n Flesch Reading Ease: {fre} ({level})")
print(f" Flesch-Kincaid Grade: {fkg}")
print(f" Avg sentence length: {avg_sentence_len} words")
print(f" Sentence variety: {variety.get('variety_rating', 'N/A')} (std dev: {variety.get('std_deviation', 0)})")
print(f" Passive voice: {passive_count} instances ({passive_rate}%)")
print(f" Avg paragraph: {para_analysis['avg_sentences']} sentences")
print(f" Long paragraphs (>5 sentences): {para_analysis['long_paragraphs']}")
if args.verbose and passive_examples:
print(f"\n Passive voice examples:")
for ex in passive_examples:
print(f" - {ex}")
# Recommendations
recs = []
if fre < 50:
recs.append("Simplify language — target Flesch 60-70 for web content")
if avg_sentence_len > 22:
recs.append("Shorten sentences — target 15-20 words average")
if passive_rate > 15:
recs.append(f"Reduce passive voice from {passive_rate}% to under 10%")
if para_analysis["long_paragraphs"] > 0:
recs.append(f"Break up {para_analysis['long_paragraphs']} long paragraphs (max 4 sentences)")
if variety.get("std_deviation", 0) < 4:
recs.append("Vary sentence length more — mix short and long for rhythm")
if recs:
print(f"\n Recommendations:")
for r in recs:
print(f" - {r}")
print()
if __name__ == "__main__":
main()
Related skills
FAQ
What steps does the pipeline cover?
Research, brief, draft, optimize (SEO and readability), and validate against a quality gate.
Does it help plan content ahead?
Yes, it manages an editorial calendar with 4-6 weeks of planned content.