一键导入
voice-configurator
Guide user through creating their voice/tone profile by analyzing recent posts. Optionally load a custom tone-of-voice document. Build a system prompt that captures their authentic voice for all generation.
菜单
Guide user through creating their voice/tone profile by analyzing recent posts. Optionally load a custom tone-of-voice document. Build a system prompt that captures their authentic voice for all generation.
Analyze Plugin 1's relevance-scored feed data to generate post ideas. Prioritizes posts scored 3+ (meaningful/strong/bullseye) as inspiration. Generates ideas equal to the user's configured posts_per_week. Rotates ideas across content pillars (40/30/20/10 mix) and suggests from the 10 proven hook formulas in the best-practice guide.
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.
Manage posting schedule, cadence, and time slots. Set how often to generate ideas, when to post, and which data sources to prioritize.
Match posted variations to actual LinkedIn performance from Plugin 1. Extract engagement metrics and feed learnings into the hook_performance learning loop.
| name | voice-configurator |
| description | Guide user through creating their voice/tone profile by analyzing recent posts. Optionally load a custom tone-of-voice document. Build a system prompt that captures their authentic voice for all generation. |
| version | 1.0.0 |
Guides user through defining their professional voice and tone. Creates a system prompt used by all generation skills.
This skill runs when:
/setup-content-engine (mandatory voice setup)/configure and selects "Voice & Tone"import sqlite3
import json
import glob
import os
from datetime import datetime, timedelta
def analyze_user_voice(db_path):
"""
Fetch user's last 20-30 own_posts and analyze writing patterns.
"""
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# Get username
cursor.execute("SELECT value FROM config WHERE key = 'linkedin_user'")
user_row = cursor.fetchone()
username = user_row[0] if user_row else "User"
# Get last 20-30 own posts
cursor.execute('''
SELECT content_short, posted_at, likes, comments FROM own_posts
WHERE posted_at IS NOT NULL
ORDER BY posted_at DESC
LIMIT 30
''')
posts = [dict(row) for row in cursor.fetchall()]
conn.close()
if not posts:
return None
# Analyze patterns
analysis = {
'sample_count': len(posts),
'posts': posts,
'avg_engagement': sum(p['likes'] + p['comments'] for p in posts) / len(posts)
}
return analysis
def profile_voice_from_posts(analysis):
"""
Use Claude to analyze writing patterns across recent posts.
"""
posts_sample = "\n\n".join([
f"Post {i+1} ({p['likes']} likes, {p['comments']} comments):\n{p['content_short']}"
for i, p in enumerate(analysis['posts'][:10]) # Sample of 10
])
analysis_prompt = f"""
Analyze these {analysis['sample_count']} recent LinkedIn posts and describe the author's writing voice:
{posts_sample}
Provide JSON with:
{{
"tone": "1-3 word description (e.g., 'professional but witty', 'mentor-like', 'thought-leader')",
"length_pref": "short/medium/long (sentence count typical)",
"emoji_usage": "none/minimal/moderate/frequent (count typical per post)",
"hashtag_usage": "none/minimal/moderate/frequent (count typical per post)",
"common_hooks": ["list", "of", "hook", "types", "used"],
"signature_phrases": ["phrases or patterns that appear often"],
"authenticity_level": "very authentic / somewhat authentic / professional but guarded",
"key_themes": ["topics", "author", "focuses", "on"],
"writing_characteristics": "Short description of what makes this voice unique"
}}
"""
result = call_claude(analysis_prompt)
return result
def present_voice_analysis(analysis, username):
"""
Present findings to user in conversational format.
"""
voice = profile_voice_from_posts(analysis)
summary = f"""
✓ I analyzed your last {analysis['sample_count']} posts and here's what I found:
**Tone:** {voice['tone']}
Your voice comes across as {voice['tone']}. You're not trying to be something you're not.
**Length:** {voice['length_pref']}
You typically write {voice['length_pref']} posts — usually {3 if voice['length_pref'] == 'short' else (5 if voice['length_pref'] == 'medium' else 8)}+ sentences.
**Emoji & Hashtag Usage:**
- Emojis: {voice['emoji_usage']} (avg {1 if voice['emoji_usage'] == 'minimal' else (3 if voice['emoji_usage'] == 'moderate' else 5)} per post)
- Hashtags: {voice['hashtag_usage']} (avg {1 if voice['hashtag_usage'] == 'minimal' else (3 if voice['hashtag_usage'] == 'moderate' else 5)} per post)
**Hook Types You Use:**
{', '.join(voice['common_hooks'])}
**What Makes Your Voice Distinctive:**
{voice['writing_characteristics']}
**Key Topics You Cover:**
{', '.join(voice['key_themes'])}
Is this accurate? Would you like to:
1. Refine these settings manually
2. Upload a custom tone-of-voice document
3. Keep these and move to authority topics
"""
return summary, voice
Present user with three paths:
Option A: Accept and Refine Analysis
Offer adjustments:
- Change tone to something more/less [current]
- Adjust length preference
- Toggle emoji/hashtag usage up or down
User can tweak each field interactively.
Option B: Upload Custom Voice Guide
User can paste or upload a markdown document. Example structure:
# My Brand Voice
## Tone
Casual but credible. I sound like a mentor, not a guru.
## Topics
Startup scaling, remote culture, founder wellness
## Writing Patterns
- Always ask a question at the end
- Use real stories from experience
- Avoid hype language
- Short, punchy sentences preferred
## Example Posts
[Link to 3 favorite posts]
Store in user_voice_config.custom_voice_doc.
Option C: Manual Entry
Let user fill in fields directly:
- Tone (free text or predefined list)
- Length (short/medium/long)
- Emoji usage (none/minimal/moderate/frequent)
- Hashtag usage (none/minimal/moderate/frequent)
def collect_authority_topics(username):
"""
Ask user to define topics they want to be known for.
"""
prompt = f"""
{username}, what topics do you want to be known for? Think about:
- Your expertise areas
- Topics you're passionate about
- What you want people to associate with your brand
Examples: "Startup scaling", "Remote culture", "Founder wellness", "AI for business"
Enter 3-5 topics (comma-separated):
"""
# User enters topics
topics = get_user_input(prompt).split(',')
topics = [t.strip() for t in topics if t.strip()]
return topics
def build_system_prompt(voice_config, authority_topics, username, custom_doc=None):
"""
Create a comprehensive system prompt for Claude to use in all generation skills.
"""
prompt = f"""You are {username}, a professional in {', '.join(authority_topics)}.
## Your Voice
Tone: {voice_config['tone']}
You write in a {voice_config['tone']} style. Your posts are {voice_config['length_pref']} ({
'1-3 sentences' if voice_config['length_pref'] == 'short' else
'3-5 sentences' if voice_config['length_pref'] == 'medium' else
'5+ sentences or threads'
}).
## Emoji & Hashtag Preferences
- Emoji usage: {voice_config['emoji_usage']} (use {
'0-1' if voice_config['emoji_usage'] == 'minimal' else
'1-3' if voice_config['emoji_usage'] == 'moderate' else
'3-5' if voice_config['emoji_usage'] == 'frequent' else '0'
} per post)
- Hashtag usage: {voice_config['hashtag_usage']} (use {
'0-1' if voice_config['hashtag_usage'] == 'minimal' else
'1-3' if voice_config['hashtag_usage'] == 'moderate' else
'3-5' if voice_config['hashtag_usage'] == 'frequent' else '0'
} per post)
## Authority Topics
You are known for expertise in: {', '.join(authority_topics)}
Write primarily about these topics. When you venture outside, connect it back.
## Writing Patterns
- Be authentic. Share real experiences and lessons.
- Avoid corporate jargon. Use conversational language.
- When asking questions, make them thoughtful, not generic.
- Use specific examples over vague claims.
## What NOT to Do
- Don't be salesy or promotional
- Don't humble-brag
- Don't oversell your insights
- Don't use stock LinkedIn phrases like "thought leader" or "synergy"
## On Hook Types
When asked to write with a specific hook (story, question, contrarian, etc.), prioritize that hook style while staying true to your voice.
{f'## Custom Voice Guide\n{custom_doc}' if custom_doc else ''}
Now write posts that sound authentically like {username}."""
return prompt
def store_voice_config(db_path, voice_config, authority_topics, system_prompt):
"""
Store in user_voice_config table (singleton, id=1)
"""
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# Check if exists
cursor.execute("SELECT id FROM user_voice_config WHERE id = 1")
exists = cursor.fetchone() is not None
authority_json = json.dumps(authority_topics)
if exists:
# Update
cursor.execute('''
UPDATE user_voice_config
SET tone = ?, length_pref = ?, emoji_usage = ?, hashtag_usage = ?,
authority_topics = ?, system_prompt = ?, updated_at = CURRENT_TIMESTAMP
WHERE id = 1
''', (
voice_config['tone'],
voice_config['length_pref'],
voice_config['emoji_usage'],
voice_config['hashtag_usage'],
authority_json,
system_prompt
))
else:
# Insert
cursor.execute('''
INSERT INTO user_voice_config
(id, tone, length_pref, emoji_usage, hashtag_usage, authority_topics, system_prompt)
VALUES (1, ?, ?, ?, ?, ?, ?)
''', (
voice_config['tone'],
voice_config['length_pref'],
voice_config['emoji_usage'],
voice_config['hashtag_usage'],
authority_json,
system_prompt
))
conn.commit()
conn.close()
Called by /setup-content-engine or /configure. Takes:
regenerate_from_posts (bool) — Re-analyze recent posts?custom_voice_doc (optional string) — User-provided tone guideoverride_values (optional dict) — Manual overrides for tone, length, etc.Returns JSON:
{
"status": "ok",
"username": "Steve Gustafson",
"voice_config": {
"tone": "mentor-like, thought-provoking",
"length_pref": "medium",
"emoji_usage": "minimal",
"hashtag_usage": "minimal"
},
"authority_topics": ["Startup Scaling", "Remote Culture", "Founder Wellness"],
"system_prompt": "[Full system prompt stored]",
"next_steps": "Run /generate to create post ideas with this voice."
}
The full voice setup takes 5-10 minutes:
Reads:
own_posts (Plugin 1, to analyze voice)config (Plugin 1, to get username)Writes:
user_voice_config (1 row, id=1)if not analysis:
return {
"error": "No posts found. Post some content first, or I can create a default voice profile.",
"offer_default": True
}
If user has no posts, offer defaults based on selected authority topics.
After voice is configured:
/setup-content-engine (if starting fresh) to set posting schedule/generate to start creating ideas/configure to adjust authority topics