| name | variation-writer |
| description | Take a post idea and generate 3 complete draft variations, each with a different hook and structure. Each variation includes 2 alternative hooks. All posts follow the bundled best-practice guide for hooks, formatting, writing tactics, and engagement mechanics.
|
| version | 3.0.0 |
Variation Writer Skill
Generates 3 distinct, ready-to-post LinkedIn drafts for each idea. Each uses a different hook formula AND a different post structure. Every draft includes 2 alternative hooks so the user can mix and match.
CRITICAL: Read the Best-Practice Guide First
Before writing ANY post, read references/best-practice-guide.md in this skill's directory. That guide is the operating manual. Every post must follow its principles — hooks, formatting, structure, writing tactics, and engagement mechanics. The guide contains 10 hook formulas, 6 post outlines, formatting rules, and checklists that directly shape output quality.
Triggers
- User says "yes" to an idea from idea-generator
- User runs
/generate and selects ideas to develop
- User requests "regenerate" for an existing idea
Step-by-Step Instructions
Step 1: Load the Idea, Voice, and Scored Source Posts
import sqlite3, json, os, glob
for db_path in glob.glob('/sessions/*/mnt/*/LinkedIn Feed Tracker/feeds.db'):
break
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
idea = dict(conn.execute("SELECT * FROM content_ideas WHERE id = ?", (idea_id,)).fetchone())
voice = conn.execute("SELECT * FROM user_voice_config WHERE id = 1").fetchone()
if voice:
voice = dict(voice)
system_prompt = voice.get('system_prompt', '')
authority_topics = json.loads(voice.get('authority_topics', '[]'))
else:
system_prompt = ''
authority_topics = []
source_ids = json.loads(idea.get('source_post_ids', '[]'))
source_posts = []
if source_ids:
placeholders = ','.join(['?'] * len(source_ids))
source_posts = [dict(r) for r in conn.execute(f"""
SELECT author_name, content, content_short, likes, comments, relevance_score, focus_areas
FROM posts WHERE id IN ({placeholders})
""", source_ids).fetchall()]
try:
hook_perf = [dict(r) for r in conn.execute("""
SELECT hook_type, avg_likes, avg_comments, sample_count, confidence
FROM hook_performance
ORDER BY confidence DESC
""").fetchall()]
except:
hook_perf = []
conn.close()
Step 2: Select 3 Hook Formulas + 3 Post Structures
From the best-practice guide, pick 3 DIFFERENT combinations. Never repeat a hook formula or structure across the 3 versions.
10 Hook Formulas (Section 11 of guide):
- Curiosity Gap — hint at valuable info without revealing it
- Quantified Promise — lead with specific number + measurable outcome
- Contrarian / Hot Take — challenge a widely accepted belief
- Story Opener — start with a specific, vulnerable moment
- Problem Spotlight — name a specific pain the audience experiences
- Bold Claim — make a confident, measurable promise
- Data Drop — open with a surprising statistic
- Micro-Confession — brief personal admission before pivoting to expertise
- Framework Tease — introduce a proprietary system or mental model
- Pattern Interrupt — break the expected pattern to force attention
6 Post Structures (Section 12 of guide):
- Narrative Arc — Hook → Context → Conflict → Turning point → Outcome → Lesson → CTA
- PSR (Problem-Solution-Result) — Hook → Problem → Solution → Result → Takeaway
- Contrarian Take — Hook → Why they're wrong → What actually works → CTA
- Tactical Listicle — Hook → Setup → 3-7 numbered points → Highlight best → CTA
- Comparison/Contrast — Hook → X does this / Y does this → Pairs → Takeaway
- Observation + Insight — Hook → What you're seeing → Why it's happening → What it means → CTA
Selection strategy:
- If hook_performance data exists, weight toward historically strong hooks for this user
- Always ensure diversity — 3 different hooks AND 3 different structures
- If the idea came from a focus area with high-scoring source posts, use those posts' themes as fuel (but never copy them)
Step 3: Write 3 Complete Drafts
For each version, write a complete, ready-to-copy-paste LinkedIn post plus 2 alternative hooks.
Non-negotiable quality standards (from the guide):
Hook (first 2 lines):
- Creates a curiosity gap, makes a bold claim, or names a specific pain
- Would stop the user from scrolling if they saw it in their feed
- Stays under ~150 characters to land before LinkedIn's "See more" fold
Structure:
- Line break after the hook — ALWAYS
- Max 1-2 sentences per paragraph with blank lines between every paragraph
- Total post under 3,000 characters
- Follows one of the 6 proven outlines
Writing:
- Specific over vague — concrete numbers, timeframes, names, examples
- Every claim backed by a result, data point, or named example
- Reads like the person talking, not a press release
- 6th-8th grade reading level — short sentences, common words
- Use the scored source posts' themes as fuel (reference what the network is discussing)
Engagement:
- Ends with a clear, specific CTA question (NOT "What do you think?" — something the reader can answer in one sentence)
- No engagement bait
- No external links in the post body (note to add links as first comment)
Formatting (exactly as it would appear on LinkedIn):
- Bold sparingly — 1-2 key phrases max
- Emojis: match user's emoji_usage setting (0-3 per post)
- Symbols (→ ✓ •) for visual structure, 2-3 per post max
- 3 or fewer hashtags at the bottom, or none (match user's hashtag_usage)
Voice:
- Follow the user's system_prompt voice exactly
- Match their length_pref setting
- The post must feel like something the user would actually write
- 70/30 rule: 70% personal experience/observation, 30% broader insight
Step 4: Present Each Draft with Alt Hooks
For each of the 3 versions, present:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
VERSION [N] — [HOOK FORMULA NAME] + [STRUCTURE NAME]
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
PRIMARY HOOK:
[The main hook — this is the default opening]
ALT HOOK A:
[A different opening using a different formula from the 10]
ALT HOOK B:
[Another option — different angle or emotional register]
───────────────────────────────────────────
[Full post text using the primary hook, formatted exactly as it would appear on LinkedIn — short paragraphs, line breaks, symbols, ready to copy-paste]
───────────────────────────────────────────
[X] words | [Hook formula] + [Structure] | Inspired by [N] posts scoring [X]+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
After showing all 3 versions, present the pre-post checklist from the guide (Section 13):
PRE-POST CHECKLIST:
□ Hook test — would you stop scrolling for these first 2 lines?
□ Specificity — concrete numbers, timeframes, examples?
□ One idea — single clear takeaway?
□ Format — short paragraphs, line breaks, mobile-readable?
□ Voice — read it out loud, does it sound like you?
□ CTA — clear, specific question at the end?
□ Proof — every claim backed by result or data?
□ Links — moved to first comment (not in post body)?
Then ask:
Pick a version:
- "1", "2", or "3" — Mark as your pick
- "1 with hook B" — Use version 1 but swap in alt hook B
- "blend 1+3" — Combine elements from two versions
- "tweak 2: [instructions]" — Edit a specific version
- "regenerate" — 3 new versions with different hooks/structures
- "next" — Move to the next idea
Step 5: Store Variations in Database
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
cursor.execute("""CREATE TABLE IF NOT EXISTS content_variations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
idea_id INTEGER NOT NULL,
hook_type TEXT NOT NULL,
structure_type TEXT,
full_text TEXT NOT NULL,
headline TEXT,
alt_hooks TEXT,
self_score REAL,
actual_performance_json TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
posted_at DATETIME,
FOREIGN KEY (idea_id) REFERENCES content_ideas(id)
)""")
for draft in drafts:
cursor.execute("""
INSERT INTO content_variations
(idea_id, hook_type, structure_type, full_text, headline, alt_hooks)
VALUES (?, ?, ?, ?, ?, ?)
""", (
idea_id,
draft['hook_type'],
draft['structure_type'],
draft['full_text'],
draft['full_text'].split('\n')[0][:80],
json.dumps(draft['alt_hooks'])
))
conn.commit()
conn.close()
Step 6: Mark User's Selection
When user picks a version:
- Update
content_ideas.status to 'used'
- Update
content_ideas.used_at to now
- If user picked an alt hook, note which one in
content_ideas.notes
Timing Recommendation
After the user picks their versions, suggest optimal posting times based on the guide (Section 7):
SUGGESTED POSTING SCHEDULE:
Based on your audience and the research:
[Day], [Time window] — [Topic/Version] — [Reason]
Example: "Wednesday, 3-5 PM — Version 1 (Story hook) — midweek afternoon peaks for B2B"
Remember:
- Space posts at least 18 hours apart
- Add a first comment within 2 minutes of posting (seed engagement, house any links)
- Stay active replying to comments for the first 60 minutes — that's the algorithm's test window
Regeneration
If user says "regenerate":
- Pick 3 NEW hook formulas (different from the originals)
- Pick 3 NEW structures (different from the originals)
- Write 3 fresh versions
- Present again with same format
Integration with linkedin-post-dev Skill
If the user has the linkedin-post-dev Cowork skill installed, the variation-writer should follow the same output conventions: 3 versions per topic, each with primary hook + 2 alt hooks, formatted for direct copy-paste. The key difference is that Plugin 2's input comes from scored feed data rather than manually provided topics.
Error States
| Error | Message |
|---|
| Idea not found | "Idea [ID] doesn't exist. Run /generate to create ideas first." |
| No voice config | "Run /setup-content-engine to configure your voice before generating drafts." |
| Idea already used | "This idea was already used on [date]. Want to regenerate anyway?" |
| Best-practice guide missing | "Warning: best-practice-guide.md not found in references/. Posts will use default quality standards." |