mit einem Klick
calendar-manager
Manage posting schedule, cadence, and time slots. Set how often to generate ideas, when to post, and which data sources to prioritize.
Menü
Manage posting schedule, cadence, and time slots. Set how often to generate ideas, when to post, and which data sources to prioritize.
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.
Match posted variations to actual LinkedIn performance from Plugin 1. Extract engagement metrics and feed learnings into the hook_performance learning loop.
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.
| name | calendar-manager |
| description | Manage posting schedule, cadence, and time slots. Set how often to generate ideas, when to post, and which data sources to prioritize. |
| version | 1.0.0 |
Configures posting schedule and manages when ideas get presented to the user.
This skill runs when:
/setup-content-engine (set initial schedule)/calendar to view/edit schedule/configure and selects "Posting Schedule"import sqlite3
import json
from datetime import datetime, timedelta
def load_current_schedule(db_path):
"""Fetch current posting_schedule (singleton, id=1)"""
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
cursor.execute("SELECT * FROM posting_schedule WHERE id = 1")
schedule = cursor.fetchone()
conn.close()
if schedule:
return {
'cadence': schedule['cadence'],
'time_slots': json.loads(schedule['time_slots_json']) if schedule['time_slots_json'] else [],
'data_source_pref': schedule['data_source_pref'],
'next_post_time': schedule['next_post_time']
}
else:
return None
def collect_schedule_preferences():
"""
Prompt user for schedule configuration.
"""
print("""
CONFIGURE YOUR POSTING SCHEDULE
1. How often should I generate new ideas?
- daily (new ideas every 24h)
- 3x-weekly (Mon, Wed, Fri)
- weekly (once per week)
- as-needed (you'll request manually)
""")
cadence = get_user_choice(['daily', '3x-weekly', 'weekly', 'as-needed'])
print("""
2. What times should I generate ideas? (local time, HH:MM format)
Examples: 09:00, 14:00, 20:00
Enter multiple times comma-separated, or press Enter for defaults (9:00, 14:00):
""")
time_input = get_user_input()
if time_input.strip():
time_slots = [t.strip() for t in time_input.split(',')]
else:
time_slots = ['09:00', '14:00']
print("""
3. What data should I focus on?
- all-posts (all network activity, last 7 days)
- high-engagement (only posts with 30+ likes)
- recent-only (last 3 days, trending now)
""")
data_source = get_user_choice(['all-posts', 'high-engagement', 'recent-only'])
return {
'cadence': cadence,
'time_slots': time_slots,
'data_source_pref': data_source
}
def store_schedule(db_path, schedule_config):
"""
Store in posting_schedule table (singleton, id=1)
"""
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
time_slots_json = json.dumps(schedule_config['time_slots'])
# Check if exists
cursor.execute("SELECT id FROM posting_schedule WHERE id = 1")
exists = cursor.fetchone() is not None
if exists:
# Update
cursor.execute('''
UPDATE posting_schedule
SET cadence = ?, time_slots_json = ?, data_source_pref = ?,
updated_at = CURRENT_TIMESTAMP
WHERE id = 1
''', (
schedule_config['cadence'],
time_slots_json,
schedule_config['data_source_pref']
))
else:
# Insert
cursor.execute('''
INSERT INTO posting_schedule
(id, cadence, time_slots_json, data_source_pref)
VALUES (1, ?, ?, ?)
''', (
schedule_config['cadence'],
time_slots_json,
schedule_config['data_source_pref']
))
conn.commit()
conn.close()
def calculate_next_generation_time(cadence, time_slots):
"""
Based on cadence, determine when next idea generation should run.
"""
now = datetime.now()
if cadence == 'daily':
# Next slot tomorrow
next_time = now.replace(hour=int(time_slots[0].split(':')[0]), minute=int(time_slots[0].split(':')[1]), second=0)
if next_time <= now:
next_time += timedelta(days=1)
elif cadence == '3x-weekly':
# Next occurrence of Mon/Wed/Fri
target_days = {0, 2, 4} # Mon, Wed, Fri
days_ahead = [(d - now.weekday()) % 7 for d in target_days]
days_ahead = [d for d in days_ahead if d > 0]
days_ahead.sort()
days_ahead = days_ahead + [days_ahead[0] + 7] if days_ahead else [1, 3, 5]
next_day = now + timedelta(days=days_ahead[0])
next_time = next_day.replace(hour=int(time_slots[0].split(':')[0]), minute=int(time_slots[0].split(':')[1]), second=0)
elif cadence == 'weekly':
# Same day next week
next_time = now + timedelta(days=7)
next_time = next_time.replace(hour=int(time_slots[0].split(':')[0]), minute=int(time_slots[0].split(':')[1]), second=0)
else: # as-needed
next_time = None
return next_time
def show_calendar_view(db_path, days_ahead=30):
"""
Show upcoming ideas and posting schedule.
"""
conn = sqlite3.connect(db_path)
conn.row_factory = sqlite3.Row
cursor = conn.cursor()
# Get all upcoming ideas
cursor.execute('''
SELECT id, topic, hook_type, status FROM content_ideas
WHERE status IN ('generated', 'presented')
ORDER BY generated_at DESC
LIMIT 10
''')
ideas = [dict(row) for row in cursor.fetchall()]
# Get variations for each
for idea in ideas:
cursor.execute('''
SELECT id, hook_type, self_score, plugin3_score FROM content_variations
WHERE idea_id = ?
LIMIT 3
''', (idea['id'],))
idea['variations'] = [dict(row) for row in cursor.fetchall()]
# Get schedule
cursor.execute("SELECT cadence, time_slots_json FROM posting_schedule WHERE id = 1")
schedule = cursor.fetchone()
conn.close()
output = f"""
POSTING CALENDAR
Schedule: {schedule['cadence'] if schedule else 'Not configured'}
Time slots: {', '.join(json.loads(schedule['time_slots_json'])) if schedule and schedule['time_slots_json'] else 'None'}
UPCOMING IDEAS ({len(ideas)} ready):
"""
for i, idea in enumerate(ideas[:5], 1):
output += f"\n{i}. {idea['topic']} ({idea['hook_type']} hook)\n"
for var in idea['variations']:
score = var['plugin3_score'] if var['plugin3_score'] else var['self_score']
output += f" - {var['hook_type']}: score {score:.2f}\n"
return output
Called by /calendar command. Takes:
action — 'view', 'edit', 'schedule', 'auto-schedule'schedule_config (optional) — Config to saveReturns JSON:
{
"status": "ok",
"schedule": {
"cadence": "3x-weekly",
"time_slots": ["09:00", "14:00"],
"data_source_pref": "high-engagement",
"next_post_time": "2026-03-24T09:00:00"
},
"upcoming_ideas": 8,
"next_generation": "2026-03-24T09:00:00"
}
Reads:
posting_schedule (current config)content_ideas (upcoming ideas)content_variations (scores for display)Writes:
posting_schedule (updates config)content_ideas (updates presented_at when scheduled)Calendar uses user's local timezone. If stored in config, use that. Otherwise, infer from browser.
User can:
/generate to create fresh ideas