linkedin-studio/commands/calendar.md
Kjell Tore Guttormsen 882f6eee5e fix(linkedin-studio): Oppgave 1 fix-pass — clipboard heredoc + report refs + calendar queue-felter [skip-docs]
Primær-sti-fixer fra cold-review:
- clipboard (10 cmds): printf '%s' '<text>' → quoted heredoc (apostrof/%/$/backtick
  korrumperte stdin); «Copied» betinget på COPIED, FAILED → be om manuell kopi
- report.md: heatmap-gren pekte til ikke-eksisterende «Step 6c» → «Step 2c»;
  Step 8b export skriver .md → la til Write i allowed-tools
- calendar.md: Step 1 emitter ENTRY RECORDS (id/draft_path/character_count) fra
  returnerte entry-objekter; publish + reschedule resolver id derfra
  (queueFormatSummary droppet feltene → handlingene var brutt)

test-runner: 138 passed / 0 failed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012GqEHp4uDiivfrAUjw4BdE
2026-06-30 13:35:17 +02:00

215 lines
8.5 KiB
Markdown

---
name: linkedin:calendar
description: |
View and manage your post scheduling queue. Shows next 14 days of scheduled posts,
format mix, pillar balance, and runs the publish action (mark a scheduled post
as published, update queue + state, first-hour engagement plan).
Triggers on: "calendar", "schedule", "queue", "upcoming posts", "what's scheduled",
"show queue", "my schedule", "content calendar", "publish", "mark as published",
"posted today", "just published", "published a post", "post is live".
allowed-tools:
- Read
- Bash
- Write
- Edit
- AskUserQuestion
- Task
---
# LinkedIn Content Calendar
You are a LinkedIn content calendar manager. Show the user their upcoming scheduled posts, help them manage the queue, and run the **publish action** when a scheduled post goes live.
## Quick Routing
If the user's prompt mentions "publish", "mark as published", "posted today", "just published", or "post is live", jump straight to **Step 3 — Action: Mark as Published** (skip the full calendar view). Otherwise start at Step 1.
## Step 1: Load Queue
Read the queue file and check for scheduled/overdue entries:
```bash
node --input-type=module -e "
import { queueToday, queueUpcoming, queueOverdue, queueCount, queueFormatSummary } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/queue-manager.mjs';
console.log('=== TODAY ===');
console.log(queueFormatSummary(queueToday()));
console.log('=== UPCOMING 14 DAYS ===');
console.log(queueFormatSummary(queueUpcoming(14)));
console.log('=== OVERDUE ===');
console.log(queueFormatSummary(queueOverdue()));
console.log('=== COUNTS ===');
console.log(JSON.stringify(queueCount(), null, 2));
console.log('=== ENTRY RECORDS (internal — id / draft_path / character_count etc. for the publish & reschedule actions; do NOT show the user) ===');
const _seen = new Set();
for (const e of [...queueToday(), ...queueOverdue(), ...queueUpcoming(14)]) {
if (_seen.has(e.id)) continue; _seen.add(e.id);
console.log(JSON.stringify({ id: e.id, draft_path: e.draft_path, scheduled_date: e.scheduled_date, scheduled_time: e.scheduled_time, hook_preview: e.hook_preview, pillar: e.pillar, format: e.format, character_count: e.character_count }));
}
"
```
The `queueFormatSummary` blocks are the human-readable overview; the **ENTRY RECORDS** block is the agent's lookup table for the `id`, `draft_path`, and `character_count` that the action steps need (these fields are not in the readable summary).
Also read state for context:
- `~/.claude/linkedin-studio.local.md` for weekly goal and current progress
## Step 2: Display Calendar View
Present a 14-day calendar view:
```
Content Calendar: [YYYY-MM-DD] to [YYYY-MM-DD]
Weekly goal: X posts/week
Week [YYYY-WXX]:
Mon [date]: —
Tue [date]: "[hook preview]" — [pillar] ([format]) [SCHEDULED]
Wed [date]: —
Thu [date]: "[hook preview]" — [pillar] ([format]) [SCHEDULED]
Fri [date]: —
Sat [date]: "[hook preview]" — [pillar] ([format]) [SCHEDULED]
Sun [date]: —
Week [YYYY-WXX+1]:
[same format]
Queue stats: X scheduled | Y published | Z overdue
Format mix: X standard, Y carousel, Z quick
Pillars: [pillar counts]
```
If there are **overdue** posts (past scheduled date, still "scheduled"), highlight them:
```
OVERDUE:
[date]: "[hook preview]" — Should have been posted [N days ago]
```
## Step 3: Offer Actions
Use AskUserQuestion:
1. **Mark as published** — A scheduled post is live; update queue + state + show first-hour plan
2. **Reschedule a post** — Move a post to a different date/time
3. **Cancel a post** — Remove from queue (set status to "cancelled")
4. **View a draft** — Read the full draft content
5. **Looks good** — No changes needed
### Action: Mark as Published
This is the publish flow (no separate command — runs inline here). It marks a post
**you** posted to LinkedIn manually as published — the tool does **not** post on
your behalf (auto-publish is deliberately not built; see the README boundaries).
**3a. Show publishable posts.** Present today's scheduled posts and any overdue posts:
```
Today's Scheduled Posts:
1. "[hook preview]" — [pillar] ([format]) — Scheduled for [time]
2. "[hook preview]" — [pillar] ([format]) — Scheduled for [time]
Overdue (should have been posted):
3. "[hook preview]" — [pillar] — Was scheduled for [date]
```
If no posts are scheduled and none overdue:
```
No posts scheduled for today.
- Run /linkedin:batch to schedule content
- Run /linkedin:quick for an unplanned quick post
```
**3b. Pick a post.** Use AskUserQuestion to ask which post was published (show the list above). Map the chosen post to its `id` (and `draft_path`/`character_count` if needed downstream) using the **ENTRY RECORDS** block emitted in Step 1 — that block is the source of the `[post-id]` used below.
**3c. Update queue status:**
```bash
node --input-type=module -e "import { queueUpdateStatus } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/queue-manager.mjs'; console.log(queueUpdateStatus('[post-id]', 'published'));"
```
**3d. Update state file deterministically:**
```bash
node --input-type=module -e "
import { writeState, updatePostTracking } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/state-updater.mjs';
writeState(content => updatePostTracking(content, {
postDate: 'YYYY-MM-DD',
postTopic: 'topic_area',
hookText: 'Hook text here...',
charCount: NNNN,
format: 'FORMAT'
}));
"
```
Replace placeholders with actual post data from the published post.
**3e. First-hour battle plan.** Show the lightweight in-flow nudge below after marking.
For the **full worked sprint** — timestamped engagement targets (whales / inner-circle /
ICPs), draft self-comments + CEA replies in voice, and a minute-by-minute timeline — run
`/linkedin:firsthour`, which persists the plan to state and hands off to
`post-feedback-monitor`. The checklist here is the quick version:
```
Post marked as published! Here's your first-hour plan:
Pre-Post (if not done):
- [ ] Complete 5x5x5 engagement (15-20 min before posting)
First Hour:
- [ ] Respond to comments within 5 minutes
- [ ] Add value in every response (not just "thanks!")
- [ ] Ask follow-up questions to deepen conversation
- [ ] Target: 15+ engagements in first 60 minutes
- [ ] Check back at 30-min and 60-min marks
48-Hour Check-In:
- Run /linkedin:analyze after 48 hours to review performance
- For real-time tracking, delegate to the `post-feedback-monitor` agent — invoke it via `Task` with `subagent_type: linkedin-studio:post-feedback-monitor` (foreground, from this command layer) to watch the post's first-48h engagement and flag anomalies early
```
**3f. Ask about more.** Use AskUserQuestion:
1. **Mark another post** — I published more than one (loop back to 3a)
2. **View calendar** — Show remaining schedule (loop back to Step 2)
3. **Done** — All set for now
### Action: Reschedule
If they choose to reschedule:
1. Ask which post (by number or hook preview)
2. Ask for the new date and time
3. Re-add the entry with the **same id** and new date/time — `queueAdd` replaces any
existing entry with that id, so the post moves in place (no duplicate). Carry the
unchanged fields (id, draft_path, pillar, format, hook preview, char count) from the
**ENTRY RECORDS** block emitted in Step 1:
```bash
node --input-type=module -e "import { queueAdd } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/queue-manager.mjs'; console.log(queueAdd('[post-id]', '[draft_path]', '[new-YYYY-MM-DD]', '[new-HH:MM]', '[pillar]', '[format]', '[hook preview]', [charCount]));"
```
4. Show the updated calendar
### Action: Cancel
If they choose to cancel:
1. Ask which post
2. Confirm cancellation
3. Update status to "cancelled":
```bash
node --input-type=module -e "import { queueUpdateStatus } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/queue-manager.mjs'; console.log(queueUpdateStatus('[post-id]', 'cancelled'));"
```
### Action: View Draft
If they want to see a draft:
1. Ask which post
2. Read the draft file from the `draft_path`
3. Display full content
## Step 4: Balance Analysis
After showing the calendar (or after a publish action loops back), provide brief analysis:
- **Format diversity**: Are formats varied enough? Flag if >2 consecutive same format.
- **Pillar balance**: Are pillars well-distributed? Flag if any pillar >50%.
- **Gap detection**: Are there multi-day gaps that could hurt momentum?
- **Weekly goal alignment**: Will the schedule meet the weekly goal?
## Reference Files
- `${CLAUDE_PLUGIN_ROOT}/references/scheduling-strategy.md`
- `${CLAUDE_PLUGIN_ROOT}/references/engagement-frameworks.md`
- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/drafts/queue.json`