Merge pull request #2 from ericosiu/add-4-skills-and-ci

feat: add 4 new skills + safety rails
This commit is contained in:
ericosiu 2026-04-04 14:40:51 -07:00 committed by GitHub
commit d2fa216bb3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 2100 additions and 0 deletions

4
Makefile Normal file
View file

@ -0,0 +1,4 @@
setup:
cp security/pre-commit-hook.sh .git/hooks/pre-commit
chmod +x .git/hooks/pre-commit
@echo "✅ Pre-commit hook installed"

View file

@ -21,6 +21,10 @@ These aren't prompts. They're complete workflows — scripts, scoring algorithms
| [**Podcast Ops**](./podcast-ops/) | One episode → 20+ content pieces across every platform | Podcast-to-Everything Pipeline, Content Calendar |
| [**Team Ops**](./team-ops/) | Ruthless performance audits and meeting intelligence | Elon Algorithm, Meeting-to-Action Extractor |
| [**Sales Playbook**](./sales-playbook/) | Value-based pricing framework that turns $10K deals into $100K deals | Pre-Call Briefing, Tiered Packager, Call Analyzer, Pattern Library |
| [**Autoresearch**](./autoresearch/) | Karpathy-inspired optimization loops for conversion content — 50+ variants, expert scoring, evolved winners | Variant Generator, Expert Panel Scorer, Evolution Engine |
| [**Deck Generator**](./deck-generator/) | AI-generated slide decks with consistent visual styles in minutes | Image Generator, Google Slides Builder, Style Presets |
| [**YT Competitive Analysis**](./yt-competitive-analysis/) | Find outlier videos and packaging patterns across any YouTube channels | Outlier Detector, Title Pattern Extractor, Channel Benchmarker |
| [**X Long-Form + Humanizer**](./x-longform-post/) | Write X articles that sound human — with a 24-pattern AI slop detector | Post Writer, Humanizer Checklist, ASCII Diagram Builder |
---

102
autoresearch/README.md Normal file
View file

@ -0,0 +1,102 @@
# Autoresearch for Marketing
**Karpathy-inspired iterative optimization loops applied to conversion content.**
Inspired by Andrej Karpathy's [autoresearch](https://github.com/karpathy/autoresearch) concept — autonomous research loops that generate, evaluate, and evolve solutions — this skill applies the same pattern to marketing copy. Instead of optimizing ML experiments, it optimizes landing pages, emails, ads, and forms.
## How It Works
```
┌────────────┐ ┌─────────────┐ ┌──────────┐
│ Generate │───►│ Score with │───►│ Evolve │
│ 10 variants│ │ 5-expert │ │ top 3 │
└────────────┘ │ panel │ └────┬─────┘
└─────────────┘ │
▲ │
└─────────────────┘
repeat until 85+
```
1. **Generate** 10+ variants of each content element (headline, CTA, body copy, etc.)
2. **Score** every variant with a 5-persona expert panel (CMO, skeptical founder, CRO expert, copywriter, your CEO)
3. **Evolve** the top performers — analyze what won, push those patterns further
4. **Cross-breed** winning elements into complete units
5. **Output** the best version + full experiment log
No traffic needed. No A/B test infrastructure. Minutes instead of weeks.
## Quick Start
```bash
# 1. Install dependencies
pip install -r requirements.txt
# 2. Set your API key
export ANTHROPIC_API_KEY="your-api-key-here"
# 3. Run the optimizer
python3 autoresearch.py --input landing-page.html --type landing_page --min-score 85
# 4. Check results
cat data/landing-page-optimization-report.md
```
## What It Optimizes
| Content Type | Elements | Score Dimensions |
|-------------|----------|-----------------|
| Landing Pages | Headline, subheadline, CTA, problem section, social proof | First impression, clarity, trust, urgency, conversion |
| Email Sequences | Subject, opening, body, CTA, PS line | Open rate, read rate, click rate, reply rate, spam risk |
| Ad Copy | Headline, description, CTA | Scroll-stopping, clarity, click-worthiness, relevance, differentiation |
| Form Pages | Headline, subtext, bullets, button, field order | First impression, trust, completion likelihood, lead quality |
## The Expert Panel
Every variant is scored by 5 simulated experts:
1. **CMO** — "Would this make me stop and engage?"
2. **Skeptical Founder** — "Do I believe this?"
3. **CRO Expert** — "Is this clear and action-driving?"
4. **Senior Copywriter** — "Is this compelling and differentiated?"
5. **Your CEO** — Configurable. Define their voice in `references/founder-voice.md`
## Output Files
Each run produces:
- `{name}-optimized.{ext}` — The winning content
- `data/{name}-experiments.json` — Full experiment log with all scores
- `data/{name}-optimization-report.md` — Human-readable summary
## Configuration
| Option | Default | Description |
|--------|---------|-------------|
| `--min-score` | 80 | Target score (stops when reached) |
| `--rounds` | 3 | Max optimization rounds |
| `--variants` | 10 | Variants per round |
| `--elements` | all | Specific elements to optimize |
| `--type` | auto | Content type override |
## Credit
Concept inspired by [Andrej Karpathy's autoresearch](https://github.com/karpathy/autoresearch) — applying autonomous iterative optimization to marketing instead of ML research.
## Requirements
- Python 3.10+
- Anthropic API key (`$ANTHROPIC_API_KEY`)
- See `requirements.txt` for Python dependencies
---
<div align="center">
**🧠 [Want these built and managed for you? →](https://singlebrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills)**
*This is how we build agents at [Single Brain](https://singlebrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills) for our clients.*
[Single Grain](https://www.singlegrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills) · our marketing agency
📬 **[Level up your marketing with 14,000+ marketers and founders →](https://levelingup.beehiiv.com/subscribe)** *(free)*
</div>

259
autoresearch/SKILL.md Normal file
View file

@ -0,0 +1,259 @@
---
name: autoresearch
description: Run Karpathy-style autoresearch optimization on any content. Generates 50+ variants, scores with a 5-expert simulated panel, evolves winners through multiple rounds, outputs optimized version + full experiment log. Use when optimizing landing pages, email sequences, ad copy, headlines, form pages, CTA text, or any conversion-focused content. Triggers on "optimize this page", "run autoresearch", "score these variants", "A/B test this copy".
---
# Autoresearch Skill
Karpathy-style optimization loops for any conversion-focused content. No traffic needed. Simulated expert panel. Minutes, not weeks.
**When to use this:** Pre-launch content optimization. Generate 50+ variants, score with 5 simulated experts, evolve winners, output the best version + full experiment log.
**When NOT to use this:** Post-launch real-traffic A/B testing — that requires real analytics, not simulated scoring.
> **The sequence:** Run autoresearch FIRST to hit 85+ simulated score. Then deploy. Then validate with real traffic.
---
## What You'll Produce
Every run outputs 3 files:
| File | Purpose |
|------|---------|
| `{name}-optimized.{ext}` | The winning optimized content |
| `data/{name}-experiments.json` | Full experiment log — all variants + all scores |
| `data/{name}-optimization-report.md` | Human-readable summary with winner rationale |
---
## Expert Panel (5 Personas)
Score every variant against all 5. Batch all variants into a **single API call** per round.
| # | Persona | Scoring Lens |
|---|---------|-------------|
| 1 | **CMO at a mid-market B2B company (50M+ revenue)** | "Would this make me stop and engage?" |
| 2 | **Skeptical founder** | "Do I believe this? Would I trust this company?" |
| 3 | **Conversion rate optimizer** | "Is this clear, specific, and action-driving?" |
| 4 | **Senior copywriter** | "Is this compelling, differentiated, and well-crafted?" |
| 5 | **Your CEO/founder** | "Direct, ROI-obsessed, no BS. Would I put this on my site?" |
> **Customization:** Replace persona #5 with your own CEO/founder voice. Define their priorities and communication style in a `references/founder-voice.md` file.
Each judge scores 0100. **Final score = average across all 5 judges.**
---
## Round Structure (Per Content Element)
```
Round 1:
→ Generate 10 variants of the element
→ Batch-score all 10 with the 5-expert panel (1 API call)
→ Rank by average score
→ Keep top 3
Round 2 (Evolution):
→ Analyze what the top 3 did right
→ Generate 10 new variants that push those winning patterns further
→ Batch-score all 10 (1 API call)
→ Keep top 3
Round 3 (If score < threshold):
→ Identify weakest scoring dimension
→ Generate 10 variants optimized for that dimension
→ Batch-score → keep top 1
Multi-element cross-breeding:
→ Take top 1 winner from each element
→ Generate 5 combinations that mix winning elements
→ Score holistically as complete units
→ Output the single best combination
```
**Stop condition:** Top variant hits minimum score threshold (default: 80) OR 3 rounds complete.
---
## Content Types & Score Dimensions
### Landing Pages
**Elements to optimize:** Hero headline, subheadline, CTA text, problem section, social proof
**Score dimensions:**
- `first_impression` — Does it grab immediately?
- `clarity` — Is the offer instantly understood?
- `trust` — Does it feel credible?
- `urgency` — Is there a reason to act now?
- `would_convert` — Would the judge actually click?
### Email Sequences
**Elements to optimize:** Subject line, opening line, body copy, CTA, PS line
**Score dimensions:**
- `would_open` — Subject line pass rate
- `would_read` — Does the opening hook?
- `would_click` — Is the CTA compelling?
- `would_reply` — Does it feel personal enough to respond to?
- `spam_risk` — Does it feel spammy? (lower = better; invert for final score)
### Ad Copy
**Elements to optimize:** Headline, description, CTA
**Score dimensions:**
- `scroll_stopping` — Does it interrupt the scroll?
- `clarity` — Is the value prop clear in 3 seconds?
- `click_worthiness` — Does the judge want to click?
- `relevance` — Does it match likely audience intent?
- `differentiation` — Does it stand out from competitors?
### Form Pages
**Elements to optimize:** Headline, subtext, value prop bullets, button text, field order, thank-you copy
**Score dimensions:**
- `first_impression` — Does it feel worth filling out?
- `trust` — Do they believe their info is safe and the offer is real?
- `completion_likelihood` — Would the judge start filling it out?
- `lead_quality` — Would this attract serious prospects (not tire-kickers)?
- `would_fill_out` — Final gut check: would they submit?
---
## Step-by-Step Execution Protocol
### Step 1: Intake & Parse
Read the source content. Identify content type automatically or confirm with user:
- HTML file → landing page or form page
- Markdown / plain text → email or ad copy
- If ambiguous, ask: "Is this a landing page, email sequence, ad copy, or form page?"
Extract all optimizable elements. List them back to user:
```
Found 5 elements to optimize:
1. Hero headline: "We help B2B companies grow"
2. Subheadline: "Full-service digital marketing..."
3. CTA: "Get Started"
4. Problem statement: [excerpt]
5. Social proof: [excerpt]
Optimizing: all | Variants per round: 10 | Min score: 80
```
### Step 2: Get API Key
Check for Anthropic API key: `$ANTHROPIC_API_KEY` environment variable.
```bash
export ANTHROPIC_API_KEY="your-api-key-here"
```
### Step 3: Run Optimization Rounds
For each element, run the round structure above.
**Critical API efficiency rule:** ALWAYS batch all variants into a single prompt. Never call the API once per variant. A round with 10 variants = 1 API call.
Model preference (in order):
1. `claude-sonnet-4-5` (preferred — fast + smart)
2. `claude-opus-4` (if highest quality needed)
3. Any claude-3.5+ model if the above aren't available
### Step 4: Cross-Breed (Multi-Element)
After all elements have winners:
1. Assemble the top winner from each element into a complete unit
2. Generate 5 holistic variants that naturally combine the winning elements
3. Score the complete units (not just individual parts)
4. Pick the winner with the highest holistic score
### Step 5: Write Output Files
```bash
# Create output directory
mkdir -p data
# Write optimized content
# Write experiments JSON
# Write optimization report
```
**Experiments JSON structure:**
```json
{
"run_id": "autoresearch-{name}-{timestamp}",
"content_type": "landing_page",
"source_file": "path/to/original",
"min_score_threshold": 80,
"rounds": [
{
"round": 1,
"element": "hero_headline",
"variants": [
{
"id": 1,
"text": "...",
"scores": {
"cmo": 72,
"skeptical_founder": 68,
"cro": 75,
"copywriter": 70,
"founder": 65
},
"avg_score": 70
}
],
"top_3": [1, 4, 7],
"winner_score": 82
}
],
"final_winner": {
"hero_headline": "...",
"subheadline": "...",
"cta": "...",
"holistic_score": 87
}
}
```
### Step 6: Report Back
Summarize results to user:
- Final winning score
- Biggest score jump (which element improved most)
- Top 2 runner-up alternatives (in case winner doesn't feel right)
- Path to all 3 output files
- Clear next step
---
## User Options
| Option | Default | Description |
|--------|---------|-------------|
| `elements` | all | Which elements to optimize |
| `variants_per_round` | 10 | How many variants to generate per round |
| `min_score` | 80 | Stop when this score is hit |
| `rounds` | 3 | Max rounds before stopping |
| `auto_apply` | false | Whether to overwrite the source file with winners |
| `content_type` | auto-detect | Force a content type if auto-detect is wrong |
---
## Quality Gates
- **< 70:** Don't ship. Something fundamental is broken.
- **70-79:** Marginal. One more round targeting the lowest-scoring dimension.
- **80-84:** Good. Shippable. Validate with real traffic.
- **85-89:** Strong. Ship with confidence.
- **90+:** Rare. Ship immediately.
---
## Anti-Patterns to Avoid
- **Never call the API once per variant.** Always batch. A 10-variant round = 1 call.
- **Don't over-optimize for one dimension.** If you're hitting 95 on clarity but 45 on trust, the overall score is misleading.
- **Don't run more than 5 rounds.** If you're not hitting 80 after 3 rounds, the problem is strategic (wrong positioning), not tactical (wrong words).
- **Don't cross-breed until each element has its own winner.** Premature cross-breeding creates incoherent combinations.

View file

@ -0,0 +1,454 @@
#!/usr/bin/env python3
"""
Autoresearch: Karpathy-inspired iterative optimization for marketing content.
Generates variants, scores with a simulated expert panel, evolves winners.
Inspired by https://github.com/karpathy/autoresearch
Usage:
python3 autoresearch.py --input landing-page.html --type landing_page
python3 autoresearch.py --input email-draft.md --type email --min-score 85
python3 autoresearch.py --input ad-copy.txt --type ad_copy --variants 15 --rounds 4
"""
import argparse
import json
import os
import sys
import time
from datetime import datetime, timezone
from pathlib import Path
try:
import anthropic
except ImportError:
print("Missing dependency: pip install anthropic", file=sys.stderr)
sys.exit(1)
# ── Content type configurations ──
CONTENT_TYPES = {
"landing_page": {
"elements": ["hero_headline", "subheadline", "cta", "problem_section", "social_proof"],
"dimensions": ["first_impression", "clarity", "trust", "urgency", "would_convert"],
},
"email": {
"elements": ["subject_line", "opening_line", "body_copy", "cta", "ps_line"],
"dimensions": ["would_open", "would_read", "would_click", "would_reply", "spam_risk"],
},
"ad_copy": {
"elements": ["headline", "description", "cta"],
"dimensions": ["scroll_stopping", "clarity", "click_worthiness", "relevance", "differentiation"],
},
"form_page": {
"elements": ["headline", "subtext", "value_bullets", "button_text", "thank_you_copy"],
"dimensions": ["first_impression", "trust", "completion_likelihood", "lead_quality", "would_fill_out"],
},
}
EXPERT_PANEL = [
{"id": "cmo", "name": "CMO at a mid-market B2B company", "lens": "Would this make me stop and engage?"},
{"id": "skeptical_founder", "name": "Skeptical founder", "lens": "Do I believe this? Would I trust this company?"},
{"id": "cro", "name": "Conversion rate optimizer", "lens": "Is this clear, specific, and action-driving?"},
{"id": "copywriter", "name": "Senior copywriter", "lens": "Is this compelling, differentiated, and well-crafted?"},
{"id": "founder", "name": "Your CEO/founder", "lens": "Direct, ROI-obsessed, no BS. Would I put this on my site?"},
]
def get_client():
"""Initialize Anthropic client from environment."""
api_key = os.environ.get("ANTHROPIC_API_KEY")
if not api_key:
print("ERROR: Set ANTHROPIC_API_KEY environment variable.", file=sys.stderr)
sys.exit(1)
return anthropic.Anthropic(api_key=api_key)
def detect_content_type(filepath: str) -> str:
"""Auto-detect content type from file extension and contents."""
ext = Path(filepath).suffix.lower()
if ext in (".html", ".htm"):
return "landing_page"
with open(filepath, "r") as f:
content = f.read(2000).lower()
if "subject" in content and ("dear" in content or "hi " in content):
return "email"
if len(content) < 500:
return "ad_copy"
return "landing_page"
def extract_elements(content: str, content_type: str) -> dict[str, str]:
"""Extract optimizable elements from content. Returns element_name -> text."""
# For a real implementation, this would parse HTML/markdown structure.
# Simplified: treat the whole content as the primary element.
config = CONTENT_TYPES[content_type]
elements = {}
# Split content into rough sections
lines = content.strip().split("\n")
if lines:
elements[config["elements"][0]] = lines[0] # First line = headline
if len(lines) > 1:
elements[config["elements"][1]] = lines[1] if len(config["elements"]) > 1 else ""
# Rest as body
remaining = "\n".join(lines[2:]) if len(lines) > 2 else ""
for elem in config["elements"][2:]:
elements[elem] = remaining
remaining = "" # Only assign to first remaining element
return {k: v for k, v in elements.items() if v}
def generate_variants(client, element_name: str, current_text: str,
content_type: str, num_variants: int,
evolution_notes: str = "", model: str = "claude-sonnet-4-5-20250514") -> list[str]:
"""Generate N variants of a content element."""
evolution_context = ""
if evolution_notes:
evolution_context = f"\n\nEvolution notes from previous round (push these winning patterns further):\n{evolution_notes}"
prompt = f"""Generate exactly {num_variants} variants of this {content_type} {element_name}.
Current text:
---
{current_text}
---
{evolution_context}
Rules:
- Each variant should be meaningfully different (not just word swaps)
- Vary approach: some direct, some curiosity-driven, some data-led, some emotional
- Keep the core value proposition intact
- Match the content type expectations for {element_name}
Return ONLY a JSON array of {num_variants} strings. No explanation, no markdown formatting.
Example: ["Variant 1 text", "Variant 2 text", ...]"""
response = client.messages.create(
model=model,
max_tokens=4096,
messages=[{"role": "user", "content": prompt}],
)
text = response.content[0].text.strip()
# Parse JSON from response
if text.startswith("```"):
text = text.split("```")[1]
if text.startswith("json"):
text = text[4:]
try:
variants = json.loads(text)
if isinstance(variants, list):
return [str(v) for v in variants[:num_variants]]
except json.JSONDecodeError:
pass
# Fallback: split by newlines
return [line.strip().strip('"').strip("- ") for line in text.split("\n") if line.strip()][:num_variants]
def score_variants(client, variants: list[str], element_name: str,
content_type: str, model: str = "claude-sonnet-4-5-20250514") -> list[dict]:
"""Score all variants with the expert panel in a single API call."""
config = CONTENT_TYPES[content_type]
dimensions = config["dimensions"]
panel_desc = "\n".join(
f" {i+1}. {e['name']} — Lens: \"{e['lens']}\"" for i, e in enumerate(EXPERT_PANEL)
)
dim_desc = ", ".join(dimensions)
variants_text = "\n".join(f" Variant {i+1}: \"{v}\"" for i, v in enumerate(variants))
prompt = f"""You are scoring {element_name} variants for a {content_type}.
Expert Panel:
{panel_desc}
Score Dimensions: {dim_desc}
Variants:
{variants_text}
For EACH variant, have each expert score it 0-100 on each dimension.
Then compute the average score across all experts and dimensions.
Return ONLY valid JSON (no markdown) in this exact format:
[
{{
"variant_id": 1,
"text": "variant text",
"expert_scores": {{
"cmo": 72, "skeptical_founder": 68, "cro": 75, "copywriter": 70, "founder": 65
}},
"dimension_scores": {{
"{dimensions[0]}": 71, ...
}},
"avg_score": 70
}},
...
]"""
response = client.messages.create(
model=model,
max_tokens=8192,
messages=[{"role": "user", "content": prompt}],
)
text = response.content[0].text.strip()
if text.startswith("```"):
text = text.split("```")[1]
if text.startswith("json"):
text = text[4:]
try:
scores = json.loads(text)
if isinstance(scores, list):
return scores
except json.JSONDecodeError:
pass
# Fallback: return empty scores
return [{"variant_id": i+1, "text": v, "avg_score": 0} for i, v in enumerate(variants)]
def run_optimization(client, element_name: str, current_text: str,
content_type: str, num_variants: int = 10,
max_rounds: int = 3, min_score: int = 80,
model: str = "claude-sonnet-4-5-20250514") -> dict:
"""Run the full optimization loop for a single element."""
rounds = []
best_score = 0
best_text = current_text
evolution_notes = ""
for round_num in range(1, max_rounds + 1):
print(f" Round {round_num}/{max_rounds}...", file=sys.stderr)
# Generate variants
variants = generate_variants(
client, element_name, current_text, content_type,
num_variants, evolution_notes, model
)
if not variants:
print(f" No variants generated, stopping.", file=sys.stderr)
break
# Score all variants (single API call)
scored = score_variants(client, variants, element_name, content_type, model)
# Sort by score
scored.sort(key=lambda x: x.get("avg_score", 0), reverse=True)
# Record round
top_3 = scored[:3]
round_data = {
"round": round_num,
"element": element_name,
"variants": scored,
"top_3_ids": [s.get("variant_id", i+1) for i, s in enumerate(top_3)],
"winner_score": top_3[0].get("avg_score", 0) if top_3 else 0,
}
rounds.append(round_data)
# Update best
if top_3 and top_3[0].get("avg_score", 0) > best_score:
best_score = top_3[0].get("avg_score", 0)
best_text = top_3[0].get("text", current_text)
print(f" Best score: {best_score}", file=sys.stderr)
# Check stop condition
if best_score >= min_score:
print(f" Hit target score {min_score}, stopping.", file=sys.stderr)
break
# Build evolution notes for next round
if top_3:
evolution_notes = "Top performers and what they did well:\n"
for s in top_3:
evolution_notes += f"- Score {s.get('avg_score', 0)}: \"{s.get('text', '')[:100]}\"\n"
time.sleep(1) # Rate limit buffer
return {
"element": element_name,
"original": current_text,
"winner": best_text,
"winner_score": best_score,
"rounds": rounds,
}
def cross_breed(client, element_winners: dict[str, dict],
content_type: str, model: str = "claude-sonnet-4-5-20250514") -> dict:
"""Cross-breed winning elements into complete units."""
elements_desc = "\n".join(
f" {name}: \"{data['winner'][:200]}\" (score: {data['winner_score']})"
for name, data in element_winners.items()
)
prompt = f"""Combine these winning {content_type} elements into 5 cohesive complete versions.
Each version should naturally integrate all winning elements while maintaining their strengths.
Winning elements:
{elements_desc}
Return JSON array of 5 objects, each with all element keys and a brief rationale:
[
{{
{', '.join(f'"{name}": "combined text"' for name in element_winners.keys())},
"rationale": "Why this combination works"
}},
...
]"""
response = client.messages.create(
model=model,
max_tokens=8192,
messages=[{"role": "user", "content": prompt}],
)
text = response.content[0].text.strip()
if text.startswith("```"):
text = text.split("```")[1]
if text.startswith("json"):
text = text[4:]
try:
combinations = json.loads(text)
if isinstance(combinations, list):
return combinations[0] if combinations else {}
except json.JSONDecodeError:
pass
return {}
def write_report(name: str, content_type: str, element_results: dict, final_score: float, output_dir: str):
"""Write the human-readable optimization report."""
report = f"""# Autoresearch Report: {name}
**Run date:** {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M UTC')}
**Content type:** {content_type}
**Final score:** {final_score}/100
## Winner
"""
for elem_name, data in element_results.items():
report += f"### {elem_name}\n{data['winner']}\n\n"
report += "## Score Progression\n\n"
report += "| Element | Round 1 Best | Final |\n"
report += "|---------|-------------|-------|\n"
for elem_name, data in element_results.items():
r1_score = data["rounds"][0]["winner_score"] if data["rounds"] else 0
report += f"| {elem_name} | {r1_score} | {data['winner_score']} |\n"
report += f"\n## Assessment\n\n"
if final_score >= 85:
report += "Ready to deploy. Validate with real traffic.\n"
elif final_score >= 75:
report += "Decent. Consider one more round targeting the weakest dimension.\n"
else:
report += "Not ready. Consider a different angle entirely.\n"
Path(output_dir).mkdir(parents=True, exist_ok=True)
report_path = os.path.join(output_dir, f"{name}-optimization-report.md")
with open(report_path, "w") as f:
f.write(report)
return report_path
def main():
parser = argparse.ArgumentParser(
description="Autoresearch: Karpathy-style optimization for marketing content"
)
parser.add_argument("--input", required=True, help="Path to content file")
parser.add_argument("--type", choices=list(CONTENT_TYPES.keys()),
help="Content type (auto-detected if omitted)")
parser.add_argument("--min-score", type=int, default=80, help="Target score threshold")
parser.add_argument("--rounds", type=int, default=3, help="Max optimization rounds per element")
parser.add_argument("--variants", type=int, default=10, help="Variants per round")
parser.add_argument("--elements", help="Comma-separated elements to optimize (default: all)")
parser.add_argument("--model", default="claude-sonnet-4-5-20250514", help="Anthropic model to use")
parser.add_argument("--output-dir", default="data", help="Output directory for results")
parser.add_argument("--name", help="Run name (default: input filename)")
args = parser.parse_args()
# Read input
input_path = Path(args.input)
if not input_path.exists():
print(f"File not found: {args.input}", file=sys.stderr)
sys.exit(1)
content = input_path.read_text()
name = args.name or input_path.stem
# Detect or use specified content type
content_type = args.type or detect_content_type(args.input)
print(f"Content type: {content_type}", file=sys.stderr)
# Extract elements
elements = extract_elements(content, content_type)
if args.elements:
selected = set(args.elements.split(","))
elements = {k: v for k, v in elements.items() if k in selected}
print(f"Elements to optimize: {list(elements.keys())}", file=sys.stderr)
# Initialize client
client = get_client()
# Run optimization for each element
element_results = {}
for elem_name, elem_text in elements.items():
print(f"\n Optimizing: {elem_name}", file=sys.stderr)
result = run_optimization(
client, elem_name, elem_text, content_type,
args.variants, args.rounds, args.min_score, args.model
)
element_results[elem_name] = result
# Cross-breed if multiple elements
if len(element_results) > 1:
print(f"\n Cross-breeding winners...", file=sys.stderr)
combined = cross_breed(client, element_results, content_type, args.model)
# Update winners with cross-bred versions if available
for elem_name in element_results:
if elem_name in combined:
element_results[elem_name]["winner"] = combined[elem_name]
# Calculate final score
scores = [r["winner_score"] for r in element_results.values()]
final_score = round(sum(scores) / len(scores), 1) if scores else 0
# Write outputs
output_dir = args.output_dir
Path(output_dir).mkdir(parents=True, exist_ok=True)
# Experiments JSON
experiments = {
"run_id": f"autoresearch-{name}-{int(time.time())}",
"content_type": content_type,
"source_file": str(input_path),
"min_score_threshold": args.min_score,
"elements": {k: v for k, v in element_results.items()},
"final_score": final_score,
}
json_path = os.path.join(output_dir, f"{name}-experiments.json")
with open(json_path, "w") as f:
json.dump(experiments, f, indent=2, default=str)
# Optimized content
optimized_path = os.path.join(output_dir, f"{name}-optimized{input_path.suffix}")
with open(optimized_path, "w") as f:
for elem_name, data in element_results.items():
f.write(f"{data['winner']}\n\n")
# Report
report_path = write_report(name, content_type, element_results, final_score, output_dir)
# Summary
print(f"\n{'='*60}", file=sys.stderr)
print(f" AUTORESEARCH COMPLETE", file=sys.stderr)
print(f" Final score: {final_score}/100", file=sys.stderr)
print(f" Optimized: {optimized_path}", file=sys.stderr)
print(f" Experiments: {json_path}", file=sys.stderr)
print(f" Report: {report_path}", file=sys.stderr)
print(f"{'='*60}", file=sys.stderr)
if __name__ == "__main__":
main()

View file

@ -0,0 +1 @@
anthropic>=0.39.0

90
deck-generator/README.md Normal file
View file

@ -0,0 +1,90 @@
# Deck Generator
**AI-generated presentations in minutes, not hours.** Every slide is a custom image in a consistent visual style.
## How It Works
```
Content Spec (JSON/MD)
┌──────────────┐
│ Style Engine │ prepend style prefix to each prompt
└──────┬───────┘
┌──────────────┐
│ Imagen 4.0 │ generate slide images (~4¢ each)
└──────┬───────┘
┌──────────────┐
│ Assemble │ local images or Google Slides
└──────────────┘
```
You provide the content. The generator creates a consistent, professional deck where every slide is a purpose-built image. No template hunting. No design skills needed.
## Quick Start
```bash
# 1. Install dependencies
pip install -r requirements.txt
# 2. Set your Gemini API key
export GEMINI_API_KEY="your-api-key-here"
# 3. Create a content spec (see examples/)
cat > slides.json << 'EOF'
[
{"name": "01-title", "prompt": "Title slide: 'AI Marketing in 2025'"},
{"name": "02-problem", "prompt": "Dashboard showing declining organic reach across platforms"},
{"name": "03-solution", "prompt": "AI agent workflow: content creation to distribution pipeline"}
]
EOF
# 4. Generate
python3 scripts/generate-deck.py --content slides.json --style whiteboard --title "AI Marketing"
```
## Style Presets
| Style | Look |
|-------|------|
| `whiteboard` | Hand-drawn sketch, black ink, orange accents |
| `corporate` | Navy/white/gold, clean sans-serif |
| `minimalist` | White background, single blue accent, Apple keynote vibe |
| `dark-tech` | Black background, neon green, terminal aesthetic |
| `playful` | Bright pastels, rounded shapes, startup energy |
| `editorial` | B&W with red spot color, magazine layout |
Or describe your own: `--style "Retro 80s neon aesthetic with grid backgrounds"`
## Cost & Speed
- ~4 cents per slide image
- 14-slide deck ≈ 56 cents and ~2 minutes
- Supports parallel generation (rate-limited)
## Output Options
1. **Local images** (default) — PNG files in output directory
2. **Google Slides** — Automatic presentation creation via Google Slides API
## Requirements
- Python 3.10+
- Gemini API key (`$GEMINI_API_KEY`)
- Google Slides API credentials (optional, for auto-assembly)
---
<div align="center">
**🧠 [Want these built and managed for you? →](https://singlebrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills)**
*This is how we build agents at [Single Brain](https://singlebrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills) for our clients.*
[Single Grain](https://www.singlegrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills) · our marketing agency
📬 **[Level up your marketing with 14,000+ marketers and founders →](https://levelingup.beehiiv.com/subscribe)** *(free)*
</div>

106
deck-generator/SKILL.md Normal file
View file

@ -0,0 +1,106 @@
---
name: deck-generator
description: Generate professional presentations with AI-generated images. Use when asked to create a deck, presentation, pitch deck, or slides. Supports style presets (whiteboard, corporate, minimalist, etc). Uses Imagen 4.0 API for image generation and Google Slides API for assembly. Produces full decks from markdown content specs in minutes.
---
# Deck Generator
Generate complete presentations where every slide is an AI-generated image in a consistent visual style.
## Quick Start
1. Read the content spec (user provides slide content or a markdown file)
2. Read `references/styles.md` to pick or customize a visual style
3. Run `scripts/generate-deck.py` with content + style
## Workflow
### Step 1: Content Spec
Accept slide content in any format. Normalize to this structure per slide:
- **Title**: Bold headline
- **Body**: Key points, stats, or narrative
- **Visual cues**: Icons, diagrams, layouts described in words
If user provides a markdown file with `---` separators, parse each section as a slide.
If user provides a topic only, generate 10-14 slides following standard deck structures.
### Step 2: Style Selection
Available style presets:
| Style | Description |
|-------|-------------|
| `whiteboard` | Hand-drawn sketch on white. Black ink, orange accents. |
| `corporate` | Navy/white/gold. Clean sans-serif. Professional. |
| `minimalist` | Pure white, electric blue accent. Maximum negative space. |
| `dark-tech` | Near-black background, neon green. Terminal aesthetic. |
| `playful` | Bright pastels, rounded shapes. Modern startup vibe. |
| `editorial` | Black/white with red spot color. Magazine aesthetic. |
Default: `whiteboard`. User can specify any preset or describe a custom style.
### Step 3: Generate
```bash
# Set your API key
export GEMINI_API_KEY="your-gemini-api-key"
# Run the generator
python3 scripts/generate-deck.py \
--content slides.json \
--style whiteboard \
--title "Deck Title" \
[--output-dir ./output] \
[--aspect 16:9]
```
The script:
1. Generates each slide image via Imagen 4.0 API
2. Saves all images to the output directory
3. Optionally creates a Google Slides presentation (requires Google Slides API credentials)
4. Returns paths to all generated images
### Step 4: Review & Iterate
To regenerate individual slides:
```bash
python3 scripts/generate-deck.py \
--content slides.json \
--style whiteboard \
--slides 3,7 \
--output-dir ./output
```
## Key Details
- **Cost**: ~4 cents per image. A 14-slide deck costs roughly 56 cents in API calls.
- **Speed**: ~2 minutes for 14 slides.
- **API**: Imagen 4.0 via Google's Generative Language API
- **Auth**: Set `$GEMINI_API_KEY` environment variable
- **Aspect ratios**: 16:9 (default), 1:1, 4:3, 3:4, 9:16
- **Image models**: `imagen-4.0-generate-001` (best quality), `imagen-4.0-fast-generate-001` (faster)
## Content JSON Format
```json
[
{"name": "01-title", "prompt": "Title slide: 'Your Deck Title' with company logo placeholder"},
{"name": "02-problem", "prompt": "Problem slide showing frustrated marketer staring at dashboard with declining metrics"},
{"name": "03-solution", "prompt": "Solution slide: AI agent workflow diagram with 3 connected boxes"}
]
```
## Google Slides Integration (Optional)
To automatically create a Google Slides presentation, set up Google Slides API credentials:
```bash
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
python3 scripts/generate-deck.py \
--content slides.json \
--style whiteboard \
--title "My Deck" \
--google-slides \
--google-account your-email@example.com
```

View file

@ -0,0 +1,3 @@
google-api-python-client>=2.100.0
google-auth>=2.23.0
google-auth-oauthlib>=1.1.0

View file

@ -0,0 +1,152 @@
#!/usr/bin/env python3
"""
Generate a presentation with AI-generated slide images.
Usage:
python3 generate-deck.py --content slides.json --style whiteboard --title "My Deck"
python3 generate-deck.py --content slides.json --style whiteboard --title "My Deck" --google-slides
Content JSON format:
[
{"name": "01-title", "prompt": "Slide-specific content description"},
{"name": "02-problem", "prompt": "Problem slide content description"},
...
]
"""
import argparse
import json
import base64
import os
import sys
import time
import urllib.request
# ── Style presets ──
STYLES = {
"whiteboard": "Hand-drawn whiteboard illustration style presentation slide. Black ink sketch on clean white background. Orange accent color for highlights. Bold hand-lettered headers. Simple stick figures and icons. No photos, no gradients, no 3D effects. Minimalist sketch aesthetic like a whiteboard drawing. ",
"corporate": "Clean professional corporate presentation slide. Navy blue and white color scheme with gold accents. Modern sans-serif typography. Flat design icons. Subtle geometric patterns in background. Professional data visualization style. No clip art. ",
"minimalist": "Ultra-minimalist presentation slide. Pure white background. Single accent color (electric blue). Large bold sans-serif text. Maximum negative space. One idea per slide. No decorative elements. Apple keynote aesthetic. ",
"dark-tech": "Dark-themed tech presentation slide. Near-black background (#0a0a0a). Neon green (#00ff88) accent color. Monospace font for headers. Terminal/code aesthetic. Subtle grid lines. Futuristic but readable. ",
"playful": "Colorful playful presentation slide. Bright pastel color palette. Rounded shapes and soft edges. Fun hand-drawn doodle elements. Friendly sans-serif font. Energetic but not childish. Modern startup aesthetic. ",
"editorial": "Editorial magazine-style presentation slide. Black and white with one spot color (red). Strong typographic hierarchy. Pull-quote style layouts. Thin serif headers, clean sans-serif body. High contrast. Vogue/Economist aesthetic. ",
}
def get_gemini_key():
"""Get Gemini API key from environment."""
key = os.environ.get("GEMINI_API_KEY")
if not key:
print("ERROR: Set GEMINI_API_KEY environment variable.", file=sys.stderr)
sys.exit(1)
return key
def generate_image(api_key, prompt, output_path, aspect="16:9", model="imagen-4.0-generate-001"):
"""Generate image via Imagen API."""
url = f"https://generativelanguage.googleapis.com/v1beta/models/{model}:predict?key={api_key}"
payload = {
"instances": [{"prompt": prompt}],
"parameters": {"sampleCount": 1, "aspectRatio": aspect}
}
req = urllib.request.Request(
url, data=json.dumps(payload).encode(),
headers={"Content-Type": "application/json"}, method="POST"
)
with urllib.request.urlopen(req, timeout=120) as resp:
data = json.load(resp)
predictions = data.get("predictions", [])
if not predictions:
raise ValueError(f"No predictions returned: {json.dumps(data)[:200]}")
img_b64 = predictions[0].get("bytesBase64Encoded", "")
if not img_b64:
raise ValueError("No image data in prediction")
img_data = base64.b64decode(img_b64)
with open(output_path, "wb") as f:
f.write(img_data)
return len(img_data)
def main():
parser = argparse.ArgumentParser(description="Generate AI-powered presentation slides")
parser.add_argument("--content", required=True, help="Path to slides JSON file")
parser.add_argument("--style", default="whiteboard", help="Style preset or custom prompt prefix")
parser.add_argument("--title", default="Generated Presentation", help="Presentation title")
parser.add_argument("--aspect", default="16:9", help="Image aspect ratio (16:9, 1:1, 4:3, etc.)")
parser.add_argument("--model", default="imagen-4.0-generate-001", help="Imagen model")
parser.add_argument("--output-dir", default="./output", help="Directory for generated images")
parser.add_argument("--slides", help="Comma-separated slide numbers to regenerate (e.g., 3,7)")
parser.add_argument("--google-slides", action="store_true", help="Create Google Slides presentation")
parser.add_argument("--google-account", help="Google account email (for Google Slides)")
args = parser.parse_args()
# Load content
with open(args.content) as f:
slides = json.load(f)
print(f"Generating {len(slides)} slides in '{args.style}' style...")
# Resolve style prefix
style_prefix = STYLES.get(args.style, args.style + " ")
# Get API key
api_key = get_gemini_key()
# Create output directory
os.makedirs(args.output_dir, exist_ok=True)
# Filter to specific slides if requested
slide_filter = None
if args.slides:
slide_filter = set(int(s) for s in args.slides.split(","))
# Generate slide images
image_paths = []
for i, slide in enumerate(slides):
if slide_filter and (i + 1) not in slide_filter:
continue
name = slide.get("name", f"slide-{i+1:02d}")
prompt = style_prefix + slide["prompt"]
img_path = os.path.join(args.output_dir, f"{name}.png")
print(f"\n--- Slide {i+1}/{len(slides)}: {name} ---")
print(f" Generating image...")
try:
size = generate_image(api_key, prompt, img_path, args.aspect, args.model)
print(f" Done ({size:,} bytes)")
image_paths.append(img_path)
except Exception as e:
print(f" Failed: {e}")
image_paths.append(None)
time.sleep(1) # Rate limit buffer
successful = sum(1 for p in image_paths if p)
print(f"\n{successful}/{len(slides)} slides generated in {args.output_dir}/")
# Cost estimate
cost = len(slides) * 0.04
print(f"Estimated cost: ${cost:.2f}")
# Summary JSON
summary = {
"title": args.title,
"slides_generated": successful,
"slides_total": len(slides),
"style": args.style,
"output_dir": args.output_dir,
"cost_estimate": f"${cost:.2f}",
"images": [p for p in image_paths if p],
}
summary_path = os.path.join(args.output_dir, "summary.json")
with open(summary_path, "w") as f:
json.dump(summary, f, indent=2)
print(f"Summary: {summary_path}")
if __name__ == "__main__":
main()

103
x-longform-post/README.md Normal file
View file

@ -0,0 +1,103 @@
# X Long-Form Post Writer + Humanizer
Write long-form X (Twitter) articles and threads that actually sound human. Includes a 24-pattern AI writing detector that catches and eliminates AI slop before you publish.
Most AI-generated content dies on the timeline because it *reads* like AI. This skill fixes that by running every draft through a humanizer checklist based on [Wikipedia's "Signs of AI writing"](https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI-generated_content) research.
## What It Does
1. Takes a topic + contrarian angle
2. Generates a structured long-form post with ASCII diagrams
3. Runs the 24-pattern humanizer checklist
4. Rewrites any sections that trigger AI patterns
5. Outputs a post ready to paste into X
## The Humanizer
The real value here. 24 specific patterns that reveal AI-generated text:
| Pattern | Example | Fix |
|---------|---------|-----|
| Significance inflation | "pivotal moment", "is a testament" | State the fact plainly |
| Negative parallelism | "It's not X. It's Y." | Say what it IS directly |
| Banned vocabulary | "leverage", "landscape", "robust" | Use normal words |
| Vague attributions | "Experts believe..." | Name the expert or drop it |
| Promotional language | "boasts a vibrant..." | Use specific numbers instead |
| AI vocabulary clustering | 3+ banned words in one paragraph | Rewrite the paragraph |
**Scoring:** Start at 100, deduct per pattern violation. Ship at 90+. Rewrite from scratch below 75.
Full checklist with all 24 patterns and deduction weights in `SKILL.md`.
## Quick Start
Drop `SKILL.md` into your Claude Code project:
```bash
cp x-longform-post/SKILL.md your-project/.claude/skills/x-longform-post.md
```
Then ask Claude Code:
- "Write an X article about why most companies waste money on brand awareness"
- "Draft a thread on how I replaced my marketing team's manual reporting with AI agents"
- "X post: contrarian take on why SEO isn't dead, it just changed"
## Voice Customization
Default voice: direct, contrarian, data-backed.
To match your personal brand:
1. Copy `references/voice-template.md` to your project
2. Fill in your voice traits, vocabulary, and a sample post
3. Reference it when generating
## Structure
Every post follows this skeleton:
1. **Hook** (1-2 lines): Contrarian claim or surprising stat
2. **Setup** (2-3 lines): Quick credibility/context
3. **Sections**: Problem, what happened, lesson
4. **ASCII diagram**: At least one per post (renders in monospace on X)
5. **Uncomfortable truth**: The thing nobody wants to say
6. **Payoff**: Worth it? Yes, here's why.
## ASCII Diagrams
Every post includes at least one code-block diagram. These break up text walls and make systems visual:
```
Pipeline Before:
Lead → SDR → AE → Close
(60% drop)
Pipeline After:
Lead → AI Score → Route
│ │ │
▼ ▼ ▼
Nurture SDR(top) Auto-close
only (small deals)
```
## Requirements
This is a prompt-based skill (no Python dependencies). Works with any Claude Code or AI coding agent setup.
## License
MIT
---
<div align="center">
**🧠 [Want these built and managed for you? →](https://singlebrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills)**
*This is how we build agents at [Single Brain](https://singlebrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills) for our clients.*
[Single Grain](https://www.singlegrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills) · our marketing agency
📬 **[Level up your marketing with 14,000+ marketers and founders →](https://levelingup.beehiiv.com/subscribe)** *(free)*
</div>

173
x-longform-post/SKILL.md Normal file
View file

@ -0,0 +1,173 @@
---
name: x-longform-post
description: Write long-form X (Twitter) posts and threads in a founder/CEO voice. Use when drafting X articles, long tweets, thought leadership threads, or viral content. Produces contrarian, data-backed posts with ASCII diagrams and code block visuals. Includes mandatory AI humanizer pass (24-pattern detector) before finalizing.
---
# X Long-Form Post Writer
Write posts for X in your founder/CEO's authentic voice. Every post should feel like a real person wrote it — not a content team, not a bot.
See `references/founder-voice.md` for the founder voice template. Customize it with your founder's real patterns.
---
## Voice Rules
- Simple declarative sentences. Short paragraphs.
- Contrarian angles backed by specific numbers and real examples.
- No corporate speak. No "I'm excited to share." No emoji in body text.
- Open with a hook that stops the scroll — contrarian claim, surprising number, or uncomfortable truth.
- End with a payoff: uncomfortable truth → "worth it" resolution.
---
## Structure
1. **Hook** (1-2 lines) — Contrarian claim or surprising stat
2. **Setup** (2-3 lines) — Establish credibility/context fast
3. **Sections** — Each follows: problem → what actually happened → fix/lesson
4. **ASCII diagram** — At least one per post (see below)
5. **Uncomfortable truth** — The insight most people avoid
6. **Payoff** — Was it worth it? Yes, and here's why.
---
## ASCII Diagrams (MANDATORY)
Every post MUST include at least one ASCII diagram in a code block. These break up walls of text and make complex systems visual.
Use box-drawing characters:
```
┌─────────┐ ┌─────────┐ ┌─────────┐
│ Input │───►│ Process │───►│ Output │
└─────────┘ └─────────┘ └─────────┘
```
Diagram types to use:
- **System architecture** — boxes connected by arrows showing how components relate
- **Before/after** — side-by-side comparison of old vs new state
- **Flow diagrams** — decision trees, pipelines, sequences
- **Hierarchy** — org charts, priority stacks, dependency trees
- **Metrics** — simple bar charts using block characters (█ ▓ ░)
Keep diagrams:
- Under 40 chars wide (mobile rendering)
- Simple enough to parse in 3 seconds
- Labeled clearly — no ambiguous boxes
Example — system flow:
```
Input (60s)
┌──────────┐
│ Process │ step 1
└────┬─────┘
┌──────────┐
│ Dispatch │ step 2
└────┬─────┘
Output
```
Example — metrics visualization:
```
Performance by Category:
Category A ████████████ 100%
Category B ████████░░░░ 67%
Category C ░░░░░░░░░░░░ 0%
```
---
## Formatting for X
- X articles support markdown-like formatting in long posts
- Use code blocks (```) for ASCII art — they render in monospace on X
- Bold with asterisks where supported
- Keep paragraphs to 1-3 sentences max
- Line breaks between every thought
---
## Content Sources
Pull from real data whenever possible:
- Real metrics from your business
- Specific incidents and debugging stories
- Actual decisions made and why
Never fabricate metrics. Use real numbers or don't use numbers.
---
## Input Format
User provides:
- **Topic**: What the post is about
- **Angle**: The contrarian or unique framing
- **Source material**: Real examples, data, incidents (optional)
---
## Output
Deliver the complete post ready to paste into X. No preamble, no "here's your post" — just the post itself.
If the post would work better as a thread (>1500 chars), split into numbered tweets with each one standalone valuable.
---
## Reference
See `references/founder-voice.md` for extended voice examples and patterns. Customize with your founder's real voice.
---
## Humanizer Checklist (MANDATORY — Run Before Finalizing)
Before returning any X article draft, check against ALL 24 humanizer patterns. If any pattern is detected, rewrite that section.
For the full humanizer expert scoring rubric, see: `../content-ops/experts/humanizer.md`
### CRITICAL: No "Not X, It's Y" Constructions
Never write "This is not X. This is Y." or "That is not X, that is Y." or any variant. These are the #1 AI slop tell. Say what something IS directly. Don't define by negation.
### Banned Vocabulary (never use these)
delve, tapestry, landscape (abstract), leverage, multifaceted, nuanced, pivotal, realm, robust, seamless, testament, transformative, underscore (verb), utilize, whilst, keen, embark, comprehensive, intricate, commendable, meticulous, paramount, groundbreaking, innovative, cutting-edge, synergy, holistic, paradigm, ecosystem, Additionally, crucial, enduring, enhance, fostering, garner, highlight (verb), interplay, intricacies, showcase, vibrant, valuable, profound, renowned, breathtaking, nestled, stunning
### Pattern Checklist
1. ☐ No significance inflation ("pivotal moment", "stands as", "is a testament")
2. ☐ No undue notability claims (listing media mentions without context)
3. ☐ No superficial -ing phrases ("highlighting", "showcasing", "underscoring")
4. ☐ No promotional language ("boasts", "vibrant", "profound", "commitment to")
5. ☐ No vague attributions ("Experts believe", "Industry reports suggest")
6. ☐ No formulaic "despite challenges... continues to" structures
7. ☐ No AI vocabulary clustering (multiple banned words in one paragraph)
8. ☐ No copula avoidance ("serves as", "stands as" — just use "is")
9. ☐ No negative parallelisms ("It's not just X, it's Y")
10. ☐ No rule-of-three forcing (triple adjectives, triple parallel clauses)
11. ☐ No synonym cycling (varying terms for the same thing unnecessarily)
12. ☐ No false ranges ("from X to Y" on no meaningful scale)
13. ☐ No em dash overuse (max 1 per 200 words)
14. ☐ No mechanical boldface emphasis
15. ☐ No inline-header vertical lists (bolded label + colon pattern)
16. ☐ No Title Case In Every Heading
17. ☐ No emoji decoration on headings/bullets
18. ☐ No curly quotation marks
19. ☐ No collaborative artifacts ("I hope this helps", "Let me know")
20. ☐ No knowledge-cutoff disclaimers
21. ☐ No sycophantic tone ("Great question!")
22. ☐ No filler phrases ("In order to", "It is important to note")
23. ☐ No excessive hedging ("could potentially", "might have some effect")
24. ☐ No generic positive conclusions ("The future looks bright", "Exciting times ahead")
### Humanizer Scoring
Start at 100. Deduct points per the rubric in `../content-ops/experts/humanizer.md`.
- **90-100**: Human-sounding. Clean. Ship it.
- **70-89**: Minor AI tells. Quick fixes needed.
- **50-69**: Obvious AI patterns. Significant rewrite needed.
- **0-49**: Full rewrite.

View file

@ -0,0 +1,137 @@
# Founder Voice Template
**Instructions:** Fill this out with your founder's real voice patterns. The more specific and honest, the better the output. This isn't about creating a persona — it's about capturing how they actually communicate.
---
## Who This Person Is
```
Name: [Your founder's name]
Role: [CEO / Founder / CTO]
Company: [Company name and what it does]
Background: [1-2 sentences on their journey]
Platform: @[their X handle]
```
---
## Core Beliefs (Things They'd Actually Say)
These are the contrarian, ROI-driven, or counterintuitive things your founder genuinely believes. Not mission statements — real opinions:
```
- "[Direct quote or paraphrase of something they actually believe]"
- "[Another real belief, ideally one that would make some people uncomfortable]"
- "[Something they've changed their mind on]"
```
**Example (founder "Eric" as reference):**
- "Most marketing spend is waste. If you can't tie it to revenue in 90 days, cut it."
- "Agencies that don't use AI by end of 2025 won't exist by 2027."
- "I used to think headcount = progress. I was wrong."
---
## Voice Patterns
### Sentence Style
- Short declarative sentences. No throat-clearing.
- Present tense. Active voice.
- No hedging unless genuinely uncertain.
```
GOOD: "We cut the team by 60%. Revenue went up."
BAD: "We strategically optimized our headcount, which led to some positive revenue outcomes."
```
### Numbers They Use
Describe how your founder handles data:
```
- Always specific (not "significant growth" — "47% growth")
- Shows work ("[X] because [specific reason], not just because [vague reason]")
- Uses dollars when relevant
```
### Things They'd Never Say
```
- "I'm excited to share..."
- "In today's fast-paced landscape..."
- "Thrilled to announce..."
- [Add your founder's specific pet peeves here]
```
---
## Topic Authority
What does this person have real credibility on? Be specific:
```
- [Topic 1]: [Why they have authority — real experience, data, results]
- [Topic 2]: [Same format]
- [Topic 3]: [Same format]
```
**Example:**
- **AI in marketing**: Replaced 4 full-time roles with agents. Revenue held flat, costs dropped 40%.
- **Content at scale**: 14,000 newsletter subscribers, YouTube channel, daily X posts — all driven by one person + agents.
- **B2B growth**: Grew agency from zero to eight figures without VC funding.
---
## Recurring Themes
What does this person keep coming back to? What are their obsessions?
```
1. [Theme]: [Why they care about this]
2. [Theme]: [Why they care about this]
3. [Theme]: [Why they care about this]
```
---
## Post Tone Settings
Adjust these based on your founder's actual style:
| Dimension | Setting |
|-----------|---------|
| Contrarian-ness | High / Medium / Low |
| Data-driven | High / Medium / Low |
| Personal stories | Often / Sometimes / Rarely |
| Humor | Dry / Direct / None |
| Urgency | High / Medium / Low |
---
## Real Examples
Paste 3-5 real posts your founder wrote that performed well. These are the ground truth:
```
[Paste real post 1]
---
[Paste real post 2]
---
[Paste real post 3]
```
---
## What Makes a Bad Post For This Person
```
- [Thing that feels off-brand]
- [Tone that doesn't fit]
- [Topic they wouldn't weigh in on]
```
---
*This template is inspired by the writing style of founders who prioritize signal over noise. Eric Siu ([@ericosiu](https://twitter.com/ericosiu)) is one example of a founder who executes this well — direct, data-backed, no fluff.*

View file

@ -0,0 +1,55 @@
# Voice Template
Customize this for your brand voice. The X Long-Form Post skill uses this to match your writing style.
## Sentence Style
- [ ] Short declarative sentences (Hemingway-esque)
- [ ] Conversational and flowing
- [ ] Academic/analytical
- [ ] Punchy and high-energy
## Tone
- [ ] Contrarian (challenges conventional wisdom)
- [ ] Authoritative (speaks from experience)
- [ ] Vulnerable (shares failures and lessons)
- [ ] Provocative (deliberately stirs debate)
- [ ] Educational (teaches step-by-step)
## Signature Moves
List 3-5 things that make YOUR writing recognizable:
1. (e.g., "Always includes a specific dollar amount or metric")
2. (e.g., "Opens with an uncomfortable truth")
3. (e.g., "Uses ASCII diagrams to explain systems")
4. (e.g., "Ends with a 'here's what I'd do differently' section")
5.
## Words You Actually Use
List words/phrases that feel natural in YOUR vocabulary:
-
-
-
## Words You Never Use
List words/phrases that feel wrong or corporate:
-
-
-
## Example Post (Your Best One)
Paste your highest-performing X post here. The skill will pattern-match against it:
```
[Paste your post here]
```
## Topics You're Known For
What subjects does your audience expect from you?
1.
2.
3.
## Data Sources
Where do you pull real numbers from?
- (e.g., "My company's actual revenue numbers")
- (e.g., "Public earnings reports")
- (e.g., "Tools I actually use and can show screenshots of")

View file

@ -0,0 +1 @@
# Prompt-based skill — no Python dependencies

View file

@ -0,0 +1,133 @@
# YouTube Competitive Analysis
Find what's actually working on YouTube. Analyzes any set of channels, identifies outlier videos (2x+ average views), and extracts packaging patterns from winners.
No manual spreadsheet work. No guessing. Data-driven competitive intel in minutes.
## What It Does
1. Pulls recent videos from any YouTube channel(s)
2. Separates long-form from Shorts
3. Calculates per-channel averages
4. Flags outliers (videos performing 2x+ above the channel average)
5. Extracts title patterns from winners
6. Exports to console, JSON, or Google Sheets
## Quick Start
```bash
# Set your YouTube API key
export YOUTUBE_API_KEY="your-key-here"
# Analyze specific channels
python3 analyze.py "$YOUTUBE_API_KEY" --channels "@AlexHormozi,@garyvee,@CodieSanchezCT"
# Use predefined channel sets
python3 analyze.py "$YOUTUBE_API_KEY" --set ai # AI creator set
python3 analyze.py "$YOUTUBE_API_KEY" --set business # Business creator set
python3 analyze.py "$YOUTUBE_API_KEY" --set both # All channels
# Export to JSON for programmatic use
python3 analyze.py "$YOUTUBE_API_KEY" --set both --output json > results.json
# Custom lookback period
python3 analyze.py "$YOUTUBE_API_KEY" --channels "@mkbhd" --days 60
```
## Setup
1. Get a [YouTube Data API v3 key](https://console.cloud.google.com/apis/api/youtube.googleapis.com)
2. Set it as an environment variable: `export YOUTUBE_API_KEY="your-key"`
3. Install dependencies: `pip install -r requirements.txt`
## Output
### Console (default)
```
================================================================================
AI Creators — LAST 30 DAYS
================================================================================
📊 CHANNEL SUMMARY:
Alex Hormozi | 2,400,000 subs | 12 videos (3.0/wk) | L: 8 S: 4
🔥 LONG-FORM OUTLIERS (top 15):
4.2x | 1,200,000 | [Alex Hormozi] How I'd Start a Business in 2025
2025-01-15 | https://youtube.com/watch?v=...
📦 TOP TITLE PATTERNS:
8x business
6x money
5x started
```
### JSON
Full structured data including all video metadata, scores, and multipliers.
## Predefined Channel Sets
**AI Creators:** Jeff Su, Alex Finn, Riley Brown, Dan Martell, Matt Wolfe, Nate Herk, Grace Leung, Matt Berman
**Business Creators:** Alex Hormozi, Gary Vaynerchuk, Patrick Bet-David, Codie Sanchez, Leila Hormozi, Iman Gadzhi, My First Million
Edit the channel dictionaries in `analyze.py` to customize.
## Interpreting Results
| Metric | What It Means |
|--------|--------------|
| **Multiplier** | How many times above channel average (2.0x = double normal) |
| **Outlier threshold** | 2x average. Videos above this are worth studying. |
| **Title patterns** | Common words in outlier titles = proven formats |
| **Cadence** | Videos per week. Higher cadence may mean lower per-video averages. |
## Proven Packaging Formats
Based on outlier analysis, these title formats consistently overperform:
**Long-form:**
- "X, Clearly Explained" (definitive explainer)
- "X hours of Y in Z minutes" (condensed value)
- "The Laziest Way to X" (low-effort promise)
- "Give me X minutes and I'll Y" (time-boxed promise)
- "X INSANE Use Cases for Y" (listicle + power word)
**Shorts:**
- "2024 vs 2025 X" (year comparison)
- "Bad Good Great X" (tier ranking)
- "Stop doing X, do Y instead" (contrarian)
## Weekly Automation
Run weekly to surface new outliers automatically:
```bash
# Cron: every Sunday at 8am
0 8 * * 0 YOUTUBE_API_KEY="your-key" python3 /path/to/analyze.py "$YOUTUBE_API_KEY" --set both --output json > /path/to/weekly-results.json
```
## Requirements
- Python 3.8+
- YouTube Data API v3 key
- No external Python dependencies (uses only stdlib)
## License
MIT
---
<div align="center">
**🧠 [Want these built and managed for you? →](https://singlebrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills)**
*This is how we build agents at [Single Brain](https://singlebrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills) for our clients.*
[Single Grain](https://www.singlegrain.com/?utm_source=github&utm_medium=skill_repo&utm_campaign=ai_marketing_skills) · our marketing agency
📬 **[Level up your marketing with 14,000+ marketers and founders →](https://levelingup.beehiiv.com/subscribe)** *(free)*
</div>

View file

@ -0,0 +1,65 @@
---
name: yt-competitive-analysis
description: >-
Analyze YouTube channels for outlier videos and packaging patterns. Identifies
what's working (2x+ average views) across any set of channels. Use when asked for
YouTube competitive analysis, viral video patterns, or packaging/title inspiration.
---
# YouTube Competitive Analysis
Outlier detection and packaging pattern extraction for YouTube channels.
## When to Use
- User asks for YouTube competitive analysis
- User wants to find viral video patterns
- User wants packaging/title inspiration from specific creators
- User wants to track competitor YouTube performance
## Prerequisites
- YouTube Data API v3 key set as `$YOUTUBE_API_KEY`
## Usage
```bash
# Analyze specific channels
python3 analyze.py "$YOUTUBE_API_KEY" --channels "@handle1,@handle2" --days 30
# Use predefined sets
python3 analyze.py "$YOUTUBE_API_KEY" --set ai
python3 analyze.py "$YOUTUBE_API_KEY" --set business
python3 analyze.py "$YOUTUBE_API_KEY" --set both
# Export formats
python3 analyze.py "$YOUTUBE_API_KEY" --set both --output json
python3 analyze.py "$YOUTUBE_API_KEY" --set both --output console
```
## Predefined Channel Sets
**AI Creators:** Jeff Su, Alex Finn, Riley Brown, Dan Martell, Matt Wolfe, Nate Herk, Grace Leung, Matt Berman
**Business Creators:** Alex Hormozi, Gary Vaynerchuk, Patrick Bet-David, Codie Sanchez, Leila Hormozi, Iman Gadzhi, My First Million
## Output Interpretation
- **Multiplier**: Times above channel average (2.0x = double normal)
- **Outlier threshold**: 2x average. Study anything above this.
- **Title patterns**: Common words in outlier titles indicate proven formats
- **Cadence**: Videos per week. Higher cadence creators may have lower per-video averages.
## Packaging Skeletons (Proven Formats)
**Long-form:**
- "X, Clearly Explained"
- "X hours of Y in Z minutes"
- "The Laziest Way to X"
- "Give me X minutes and I'll Y"
- "X INSANE Use Cases for Y"
**Shorts:**
- "2024 vs 2025 X" (year comparison)
- "Bad Good Great X" (tier ranking)
- "Stop doing X, do Y instead" (contrarian)

View file

@ -0,0 +1,257 @@
#!/usr/bin/env python3
"""
YouTube Competitive Analysis Outlier Detection
Analyzes YouTube channels to find outlier videos (2x+ average views)
and extract packaging patterns from winners.
Usage:
python3 analyze.py <API_KEY> [--channels @h1,@h2] [--set ai|business|both] [--days 30] [--output console|json]
"""
import argparse
import json
import sys
import urllib.request
import urllib.parse
import re
import os
from collections import Counter
from datetime import datetime, timezone, timedelta
AI_CHANNELS = {
"Jeff Su": "@jeffsu",
"Alex Finn": "@AlexFinnOfficial",
"Riley Brown": "@RileyBrown",
"Dan Martell": "@danmartell",
"Matt Wolfe": "@mreflow",
"Nate Herk": "@nateherk",
"Grace Leung": "@graceleungyl",
"Matt Berman": "@matthew_berman",
}
BIZ_CHANNELS = {
"Alex Hormozi": "@AlexHormozi",
"Gary Vaynerchuk": "@garyvee",
"Patrick Bet-David": "@PatrickBetDavid",
"Codie Sanchez": "@CodieSanchezCT",
"Leila Hormozi": "@LeilaHormozi",
"Iman Gadzhi": "@ImanGadzhi",
"Sam Parr": "@MyFirstMillionPod",
}
def api_get(api_key, endpoint, params):
"""Make a YouTube Data API v3 request."""
params["key"] = api_key
url = f"https://www.googleapis.com/youtube/v3/{endpoint}?" + urllib.parse.urlencode(params)
with urllib.request.urlopen(url, timeout=30) as r:
return json.loads(r.read())
def resolve_channel(api_key, handle):
"""Resolve a YouTube handle to a channel ID and metadata."""
data = api_get(api_key, "search", {"q": handle, "type": "channel", "part": "snippet", "maxResults": 1})
if not data.get("items"):
return None, None
ch_id = data["items"][0]["snippet"]["channelId"]
stats = api_get(api_key, "channels", {"id": ch_id, "part": "statistics,snippet,contentDetails"})
if not stats.get("items"):
return ch_id, None
return ch_id, stats["items"][0]
def get_recent_videos(api_key, channel_id, cutoff, max_results=100):
"""Get all videos from a channel published after the cutoff date."""
ch_data = api_get(api_key, "channels", {"id": channel_id, "part": "contentDetails"})
uploads_id = ch_data["items"][0]["contentDetails"]["relatedPlaylists"]["uploads"]
videos = []
page_token = None
done = False
while not done and len(videos) < max_results:
params = {"playlistId": uploads_id, "part": "snippet,contentDetails", "maxResults": 50}
if page_token:
params["pageToken"] = page_token
data = api_get(api_key, "playlistItems", params)
for item in data.get("items", []):
pub = item["snippet"]["publishedAt"]
pub_dt = datetime.fromisoformat(pub.replace("Z", "+00:00"))
if pub_dt < cutoff:
done = True
break
videos.append(item["contentDetails"]["videoId"])
page_token = data.get("nextPageToken")
if not page_token:
break
if not videos:
return []
all_details = []
for i in range(0, len(videos), 50):
batch = videos[i:i + 50]
vdata = api_get(api_key, "videos", {"id": ",".join(batch), "part": "snippet,statistics,contentDetails"})
all_details.extend(vdata.get("items", []))
return [v for v in all_details if datetime.fromisoformat(v["snippet"]["publishedAt"].replace("Z", "+00:00")) >= cutoff]
def parse_duration(dur):
"""Parse ISO 8601 duration to seconds."""
m = re.match(r'PT(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?', dur)
if not m:
return 0
h, mi, s = m.groups()
return int(h or 0) * 3600 + int(mi or 0) * 60 + int(s or 0)
def analyze_channels(api_key, channels, days, set_name="Custom"):
"""Analyze a set of channels and find outlier videos."""
cutoff = datetime.now(timezone.utc) - timedelta(days=days)
results = {"set": set_name, "channels": {}, "outliers": {"long": [], "short": []}}
for name, handle in channels.items():
print(f" Analyzing {name}...", file=sys.stderr)
try:
ch_id, ch_info = resolve_channel(api_key, handle)
if not ch_id or not ch_info:
continue
subs = int(ch_info["statistics"].get("subscriberCount", 0))
videos = get_recent_videos(api_key, ch_id, cutoff, 100)
longs, shorts = [], []
for v in videos:
dur_sec = parse_duration(v["contentDetails"]["duration"])
views = int(v["statistics"].get("viewCount", 0))
entry = {
"channel": name, "handle": handle, "subs": subs,
"title": v["snippet"]["title"],
"format": "Short" if dur_sec <= 60 else "Long",
"views": views,
"likes": int(v["statistics"].get("likeCount", 0)),
"comments": int(v["statistics"].get("commentCount", 0)),
"duration_min": round(dur_sec / 60, 1),
"published": v["snippet"]["publishedAt"][:10],
"url": f"https://youtube.com/watch?v={v['id']}",
}
(shorts if entry["format"] == "Short" else longs).append(entry)
long_avg = sum(v["views"] for v in longs) / len(longs) if longs else 0
short_avg = sum(v["views"] for v in shorts) / len(shorts) if shorts else 0
for v in longs:
v["avg"] = round(long_avg)
v["multiplier"] = round(v["views"] / long_avg, 1) if long_avg > 0 else 0
if v["views"] > long_avg * 2:
results["outliers"]["long"].append(v)
for v in shorts:
v["avg"] = round(short_avg)
v["multiplier"] = round(v["views"] / short_avg, 1) if short_avg > 0 else 0
if v["views"] > short_avg * 2:
results["outliers"]["short"].append(v)
results["channels"][name] = {
"handle": handle, "subs": subs,
"total_30d": len(longs) + len(shorts),
"longs": len(longs), "shorts": len(shorts),
"long_avg": round(long_avg), "short_avg": round(short_avg),
"per_week": round((len(longs) + len(shorts)) / (days / 7), 1),
}
except Exception as e:
print(f" Error: {e}", file=sys.stderr)
results["outliers"]["long"].sort(key=lambda x: -x["multiplier"])
results["outliers"]["short"].sort(key=lambda x: -x["multiplier"])
return results
def print_console(results, days):
"""Print results in a human-readable console format."""
print(f"\n{'=' * 80}")
print(f" {results['set']} — LAST {days} DAYS")
print(f"{'=' * 80}")
print("\n📊 CHANNEL SUMMARY:")
for name, data in sorted(results["channels"].items(), key=lambda x: -x[1]["subs"]):
print(f" {name:25s} | {data['subs']:>10,} subs | {data['total_30d']:>3} videos ({data['per_week']}/wk) | L:{data['longs']:>3} S:{data['shorts']:>3}")
print(f"\n🔥 LONG-FORM OUTLIERS (top 15):")
for v in results["outliers"]["long"][:15]:
print(f" {v['multiplier']:.1f}x | {v['views']:>10,} | [{v['channel']}] {v['title'][:55]}")
print(f" {v['published']} | {v['url']}")
print(f"\n⚡ SHORT-FORM OUTLIERS (top 15):")
for v in results["outliers"]["short"][:15]:
print(f" {v['multiplier']:.1f}x | {v['views']:>10,} | [{v['channel']}] {v['title'][:55]}")
print(f" {v['published']} | {v['url']}")
# Title pattern extraction
all_titles = [v["title"] for v in results["outliers"]["long"] + results["outliers"]["short"]]
words = []
for t in all_titles:
words.extend(re.findall(r'\b\w+\b', t.lower()))
stopwords = {
'the', 'a', 'an', 'i', 'you', 'my', 'your', 'this', 'that', 'it', 'is', 'are',
'was', 'were', 'to', 'in', 'for', 'on', 'of', 'and', 'or', 'how', 'with', 'do',
'not', 'dont', 'can', 'will', 'just', 'all', 'but', 'be', 'have', 'has', 'from',
'at', 'by', 'if', 'so', 'no', 'what', 'when', 'why', 'who', 'which', 'their',
'they', 'them', 'me', 'we', 'us', 'its', 'been', 'had', 'did', 'get', 'got',
'than', 'into', 'these', 'those', 'very', 'more', 'most', 'about', 'up', 'out',
'one', 'also', 'even', 'after', 'before', 'here', 'there', 'then', 'new', 'now',
'way', 'may', 'like', 'over', 'only', 'any', 'such', 'make', 'each', 're', 've',
'll', 'don', 't', 's', 'm', 'd'
}
filtered = [w for w in words if w not in stopwords and len(w) > 2]
word_freq = Counter(filtered).most_common(15)
print(f"\n📦 TOP TITLE PATTERNS:")
for word, count in word_freq:
print(f" {count:>3}x {word}")
def main():
parser = argparse.ArgumentParser(description="YouTube Competitive Analysis — Outlier Detection")
parser.add_argument("api_key", nargs="?", default=os.environ.get("YOUTUBE_API_KEY"),
help="YouTube Data API key (or set $YOUTUBE_API_KEY)")
parser.add_argument("--channels", help="Comma-separated YouTube handles (e.g., @AlexHormozi,@garyvee)")
parser.add_argument("--set", choices=["ai", "business", "both"], help="Predefined channel set")
parser.add_argument("--days", type=int, default=30, help="Lookback period in days (default: 30)")
parser.add_argument("--output", choices=["console", "json"], default="console",
help="Output format (default: console)")
args = parser.parse_args()
if not args.api_key:
print("Error: Provide API key as argument or set $YOUTUBE_API_KEY", file=sys.stderr)
sys.exit(1)
# Build channel list
channel_sets = {}
if args.set in ["ai", "both"]:
channel_sets["AI Creators"] = AI_CHANNELS
if args.set in ["business", "both"]:
channel_sets["Business Creators"] = BIZ_CHANNELS
if args.channels:
custom = {}
for h in args.channels.split(","):
h = h.strip()
name = h.replace("@", "").title()
custom[name] = h
channel_sets["Custom"] = custom
if not channel_sets:
print("Error: Specify --channels or --set", file=sys.stderr)
sys.exit(1)
all_results = []
for set_name, channels in channel_sets.items():
print(f"\nAnalyzing {set_name}...", file=sys.stderr)
results = analyze_channels(args.api_key, channels, args.days, set_name)
all_results.append(results)
if args.output == "json":
print(json.dumps(all_results, indent=2))
else:
for results in all_results:
print_console(results, args.days)
if __name__ == "__main__":
main()

View file

@ -0,0 +1 @@
# No external dependencies — uses only Python standard library