with one click
social-calendar
// Plan weekly 14-post social media content calendar across LinkedIn, Facebook, Instagram for any active brand. Runs weekly on Sunday cron schedule.
// Plan weekly 14-post social media content calendar across LinkedIn, Facebook, Instagram for any active brand. Runs weekly on Sunday cron schedule.
[HINT] Download the complete skill directory including SKILL.md and all related files
| name | social-calendar |
| description | Plan weekly 14-post social media content calendar across LinkedIn, Facebook, Instagram for any active brand. Runs weekly on Sunday cron schedule. |
| allowed-tools | Read, Grep, Glob, Bash, WebSearch |
| Agent | Version | Last Changed |
|---|---|---|
| Link | v2.4.0 | May 07, 2026 |
Description: Plan weekly 14-post social media content calendar across LinkedIn, Facebook, Instagram for any active brand
v2.4.0 — May 07, 2026
mcp__notion__notion-* → mcp__claude_ai_Notion__notion-* (matches the actual registered tool names; old shorter form was working via undocumented fuzzy matching)v2.2.15 — May 05, 2026
v2.2.9 — April 30, 2026
v2.2.5 — April 26, 2026
Read agents/link.md before starting. It defines the active brand, personality, quality checklist, and available tools. Determine the active brand from $DEFAULT_BRAND env var — if not set, ask the user.
You are a senior social media strategist for the active brand. Your job is to plan a 1-week content calendar (next Mon–Sat) across LinkedIn, Facebook, and Instagram — optimized for the brand's primary conversion goals (read from brands/{brand}/product.md).
Runs weekly on Sunday cron schedule. Output is a planning table only — topics, formats, angles, CTAs. The content-generator skill handles actual copy.
Read these files before planning:
brands/{brand}/brand.mdbrands/{brand}/product.mdbrands/{brand}/audience.mdbrands/{brand}/competitors.mdRun /link-skills:research-strategy before planning. Focus the research on:
Read the research output from outputs/{brand}/strategy/ after it completes.
This research MUST drive the content mix and topic selection in Step 2. Do not default to a fixed content mix — adapt based on what's actually working in the market right now.
Generate exactly 14 posts for the upcoming Mon–Sat week using the fixed slot assignments below.
| Day | Platform | Format |
|---|---|---|
| Monday | Post | |
| Monday | Post | |
| Monday | Post | |
| Tuesday | Post | |
| Tuesday | Reel | |
| Wednesday | Carousel | |
| Wednesday | Post | |
| Thursday | Reel | |
| Thursday | Story | |
| Friday | Post | |
| Friday | Story | |
| Friday | Reel | |
| Saturday | Post | |
| Saturday | Carousel |
Each week has 3 Reels (Tue IG, Thu FB, Fri IG) and 2 Stories (Thu IG, Fri FB). Argil AI avatar videos are expensive — pick exactly 1 Reel per week for Argil treatment. The rest use Gemini-generated images (published as Stories). Stories always use Gemini-generated static images.
Selection criteria for the Argil Reel — pick the one most likely to drive conversions:
brands/{brand}/avatars.md)Mark the chosen Reel by adding (Argil) after the Format in the calendar table, e.g., Reel (Argil). All other Reels are just Reel (content-generator will use a Gemini-generated image published as Story).
Example: | Thursday | Facebook | Reel (Argil) | ... | — this one gets Argil avatar | Tuesday | Instagram | Reel | ... | — Gemini-generated image as Story | Friday | Instagram | Reel | ... | — Gemini-generated image as Story
| Field | Constraint |
|---|---|
| Date | DD Mon YYYY |
| Platform | LinkedIn / Facebook / Instagram |
| Format | From fixed slot table above. For the 1 chosen Argil Reel, use Reel (Argil) instead of Reel. |
| Topic | Short topic name (≤6 words) |
| Persona | One of the persona slugs defined in brands/{brand}/audience.md — max 2× per persona per week. Examples: content-mgr, seo-pro, sales-rep, agency-owner, solopreneur, growth-mktr |
| Content Angle | Hook + key message (1 sentence, ≤20 words) |
| CTA | Specific action (≤8 words) |
| Hashtags | 3–5 hashtags |
| Image Brief | Scene description for image gen (1 sentence). End with: "No text. No logos. No watermarks." |
| Direction | Template variant for the post — see "Direction selection" below. Required for IG/FB Carousel and Story/Reel posts; leave blank for LinkedIn / Reel(Argil) / formats without a Claude Design template. |
| Status | Planned |
The Direction field tells content-generator which template variant to render at production time. Direction is your responsibility (planning); content-generator just applies what you set.
For Carousel posts — combine the carousel template's two variant axes as coverVariant-bodyVariant:
type-allnumbers — default. Big typographic cover, all sign slides have large kicker numerals (01/02/03/04). Good for educational carousels with strong listicle structure.sticker-editorial — Sticker-style cover (badge / playful), editorial body slides (less number-forward). Good for personality-led / opinion / hot-take content.editorial-mixed — Editorial cover (magazine), mixed body slide treatments. Good for story / case-study carousels where each slide has a different visual rhythm.Pick by content type:
type-allnumberssticker-editorialeditorial-mixedFor Story / Reel posts — pick one of the story template's three direction styles:
A — Spotlight Dark. Brand-led campaigns: eyebrow → headline → divider pill, dark backgrounds, accent color leading. Good for product launches, brand campaigns, urgent CTAs.B — Editorial Stat. When a single big number is the story (oversized stat as hero). Good for ROI proof, benchmark posts, "X% faster" / "$Y saved" data points.C — Cream Press. Light, magazine-style. Good for case studies, testimonials, founder posts, customer stories.Pick by content type:
ABCFor LinkedIn posts, Reel(Argil), or any format without a matching Claude Design template — leave Direction blank.
If you assign a Direction the brand's template doesn't support (e.g. carousel template only ships type-allnumbers), content-generator falls back to the template defaults and logs a warning — but planning quality suffers, so check the entry HTML inside brands/{brand}/social-carousel-template/ and brands/{brand}/social-story-template/ (look for the *.html file that contains the EDITMODE-BEGIN block) for which variants the brand actually has before assigning.
Adapt the mix based on Step 1b research findings. If viral content in the niche is heavy on hot takes and opinion pieces, lean into that. If competitors are winning with educational carousels, plan more of those.
Default mix (use ONLY if research is unavailable):
Research-driven adjustments:
brands/{brand}/audience.md (e.g. content-mgr, seo-pro, sales-rep, agency-owner, solopreneur, growth-mktr)Use Notion MCP to save the calendar to Notion. The calendar page must live inside the brand's social-calendar database so content-generator Step 1 can find it via ${BRAND}_NOTION_DB.
Page title: SocialCalendar_[DDMon]-[DDMonYYYY]
e.g. SocialCalendar_17Mar-22Mar2026
This step is primarily for the first-ever calendar run for a brand. On subsequent weekly runs, the env var is already set and the DB already exists — fetch and proceed.
Read ${BRAND}_NOTION_DB from .claude/settings.local.json (e.g. FIVEBUCKS_NOTION_DB).
Decision:
IF env var is set:
fetch the DB → if fetch succeeds → DB exists → DO NOT create. Skip to Step 3b.
(only create if fetch returns 404 / not_found, meaning the DB was deleted)
IF env var is NOT set:
→ first-ever run for this brand → create the DB (instructions below).
Use mcp__claude_ai_Notion__notion-fetch:
- id: "${BRAND}_NOTION_DB"
If env var is set and fetch succeeds → skip to Step 3b. Do not create another DB.
Create only when env var is unset (or fetch returns not_found):
Use mcp__claude_ai_Notion__notion-create-database:
- parent: { "type": "page_id", "page_id": "<brand_parent_page_or_workspace_root>" }
- title: "{Brand Name} Social Media Calendar"
- properties: {
"Name": { "title": {} },
"Date Range": { "rich_text": {} },
"Status": { "select": { "options": [
{"name": "Planned"},
{"name": "Published"},
{"name": "Archived"}
] } },
"Posts": { "number": { "format": "number" } },
"Created": { "created_time": {} }
}
If no brand parent page exists yet, notion-search for "{Brand}" first; if nothing is found, create a parent page with notion-create-pages titled "{Brand Name}" at the workspace root, then nest the DB under it.
After creation, persist the new DB ID back to .claude/settings.local.json under env.{BRAND}_NOTION_DB. Read the existing settings file, add the key (preserve all other keys), write back. This makes the DB discoverable by content-generator and by every future weekly social-calendar run.
Notify the user in chat (first-run only):
Created new Notion DB {Brand Name} Social Media Calendar and saved its ID as
${BRAND}_NOTION_DBin.claude/settings.local.json. Future runs will reuse this DB — no re-creation.
Save local backup first: outputs/{brand}/strategy/SocialCalendar_[DDMon]-[DDMonYYYY].md
Resolve the DB to a data_source_url (needed for scoped search):
Use mcp__claude_ai_Notion__notion-fetch:
- id: "${BRAND}_NOTION_DB"
Extract the collection:// URL from the response — typically data_sources[0].url. Save as data_source_url.
Check if the week's page already exists, scoped to the brand DB only:
Use mcp__claude_ai_Notion__notion-search:
- query: "SocialCalendar_[DDMon]-[DDMonYYYY]"
- data_source_url: <data_source_url from step 1>
- query_type: "internal"
⚠️ Never run a bare workspace-wide notion-search for the page name — it can return another brand's calendar with the same week label. Always pass data_source_url.
If no matching page → create it:
Use mcp__claude_ai_Notion__notion-create-pages:
- parent: { "database_id": "${BRAND}_NOTION_DB" }
- pages: [{
"properties": { "Name": "SocialCalendar_[DDMon]-[DDMonYYYY]", "Status": "Planned", "Posts": 14 },
"content": "<markdown of the calendar — heading + 11-column table + content mix + persona distribution>"
}]
The content field accepts markdown. Use a markdown table with 11 columns — Date, Platform, Format, Topic, Persona, Content Angle, CTA, Hashtags, Image Brief, Direction, Status. The Notion connector converts markdown headings, tables, and paragraphs into the appropriate block types automatically.
If the page already exists → update it in place:
Use mcp__claude_ai_Notion__notion-update-page:
- page_id: <existing page id>
- properties: { "Status": "Planned", "Posts": 14 }
- replace_content: true
- content: "<same markdown as step 3>"
replace_content: true clears existing child blocks before appending the new content — avoids duplicate tables stacking up across re-runs.
Capture the returned page URL for the Slack notification.
Brand-header sanity check before Slack: the returned page's parent database title must contain the active brand. If somehow it doesn't (shouldn't happen given the DB-scoped flow), abort with a failed log — do not announce a calendar that landed in another brand's DB.
---
Date: YYYY-MM-DD
Skill Used: social-calendar
Brand: {brand}
Week: Mon DD Mon – Sat DD Mon YYYY
Status: Planned
Notion: {url}
---
# Social Calendar: [Mon DD Mon] – [Sat DD Mon YYYY]
| Date | Platform | Format | Topic | Persona | Content Angle | CTA | Hashtags | Image Brief | Direction | Status |
|---|---|---|---|---|---|---|---|---|---|---|
| 06 Apr 2026 | LinkedIn | Post | ... | ... | ... | ... | ... | ... | (blank) | Planned |
... (14 data rows)
## Content Mix
| Type | Count | Posts |
|---|---|---|
| Educational / How-to | 5 | ... |
... (5 rows)
## Persona Distribution
| Persona | Count | Posts |
|---|---|---|
| content-mgr | 2 | ... |
... (6-7 rows)
Before calling slack_send_message, you MUST first call ToolSearch with query "slack_send_message" to load the tool schema. The Slack MCP tool is deferred — calling it without loading the schema first will cause the task to hang.
DM the user via Slack MCP (slack_send_message, channel_id: "$SLACK_NOTIFY_USER"):
📅 [{brand}] Social Calendar Ready — Week of [DD Mon YYYY]
• 14 posts planned (Mon–Sat)
• Platforms: LinkedIn, Facebook, Instagram
• Mix: [x] edu / [x] proof / [x] product / [x] CTA / [x] engage
• Notion: [page URL]
(Argil) per week — no more, no lesstype-allnumbers / sticker-editorial / editorial-mixed — or whatever combos the brand's carousel template supports) and every IG/FB Story / Reel post (one of A / B / C); left blank for LinkedIn posts and Reel(Argil)See docs/new_agent_onboarding/metrics-spec.md for the full JSONB contract.
Use gateway MCP tool `fiveagents_log_run`:
- fiveagents_api_key: ${FIVEAGENTS_API_KEY}
- skill: "social-calendar"
- brand: "<active-brand>"
- status: "<success|failed>"
- summary: "<1 line, <200 chars>"
- started_at: "<ISO timestamp>"
- completed_at: "<ISO timestamp>"
- metrics: {
"date": "YYYY-MM-DD",
"week": "DD-DD Mon YYYY",
"posts_planned": 14,
"calendar_status": "Published",
"notion_url": "https://notion.so/...",
"posts": [{ "date": "DD Mon", "platform": "LinkedIn", "topic": "...", "persona": "...", "format": "static", "status": "Published" }],
"content_mix": [{ "type": "static", "count": 11, "percentage": 78.6 }],
"persona_distribution": [{ "persona": "seo-pro", "count": 3 }]
}