| 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 |
Calendar Manager Skill
Configures posting schedule and manages when ideas get presented to the user.
Triggers
This skill runs when:
- User runs
/setup-content-engine (set initial schedule)
- User runs
/calendar to view/edit schedule
- User runs
/configure and selects "Posting Schedule"
How It Works
Step 1: Load Current 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
Step 2: Collect Schedule Preferences
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
}
Step 3: Store Schedule
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'])
cursor.execute("SELECT id FROM posting_schedule WHERE id = 1")
exists = cursor.fetchone() is not None
if exists:
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:
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()
Step 4: Calculate Next Generation Time
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_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':
target_days = {0, 2, 4}
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':
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:
next_time = None
return next_time
Step 5: View Upcoming Schedule
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()
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()]
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()]
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
Input
Called by /calendar command. Takes:
action — 'view', 'edit', 'schedule', 'auto-schedule'
schedule_config (optional) — Config to save
Output
Returns 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"
}
Database Schema
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)
Time Zone Handling
Calendar uses user's local timezone. If stored in config, use that. Otherwise, infer from browser.
Next Steps
User can:
- View calendar to see upcoming posts
- Manually schedule ideas
- Auto-schedule all ready ideas
- Run
/generate to create fresh ideas