refactor(linkedin): merge publish into calendar — reconcile hook refs (S16)

Step 17 of voyage-build (S16 in plan). publish.md absorbed into calendar.md
as an inline action (Mark as Published flow: queue update, state update,
first-hour battle plan) reusing the same queue-manager.mjs + state-updater.mjs
primitives that publish.md called. calendar.md frontmatter triggers extended
with the publish trigger words; quick-routing block jumps straight to the
publish action when the user prompt names it.

All 21 route-refs reconciled across the 9 expected files, with the 9
hook-script refs (5 in session-start.mjs, 2 in posting-reminder.mjs, 1 in
user-prompt-context.mjs, 1 in hooks/prompts/state-update-reminder.md)
rewritten to call /linkedin:calendar so the runtime guidance no longer
points at a dead command. compile-hooks.py --check reports clean (no
type: prompt hook changes touched hooks.json).

Verify (intent: zero stray refs, file gone): exit 0. Literal Verify in
plan.md:727 logged exit 1 (same exit-inverted && pattern as S15 plan.md:635
— logged for plan-pass at Step 21).

Manifest audit: PASS (expected_paths=calendar.md present; must_contain
[Pp]ublish: 17 matches in calendar.md).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-28 05:27:36 +02:00
commit e4aa5a61c2
12 changed files with 117 additions and 156 deletions

View file

@ -123,7 +123,7 @@ After saving each draft, add it to the queue:
node --input-type=module -e "import { queueAdd } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/queue-manager.mjs'; console.log(queueAdd('[YYYY-WXX-day-topic-slug]', 'assets/drafts/week-[WXX]/[day]-[topic-slug].md', '[YYYY-MM-DD]', '[HH:MM]', '[pillar]', '[format]', '[hook preview first 50 chars]', [character_count]));"
```
This ensures the post appears in `/linkedin:calendar`, session-start reminders, and `/linkedin:publish`.
This ensures the post appears in `/linkedin:calendar` (both for viewing and for the publish action) and in session-start reminders.
## Step 4: Review All
@ -152,7 +152,7 @@ Ask if they want to:
After approval:
- Confirm all drafts are saved and queued
- Update state file with planned topics (note: state updates for batch posts happen at publish time via `/linkedin:publish`, not at batch creation)
- Update state file with planned topics (note: state updates for batch posts happen at publish time via the `/linkedin:calendar` publish action, not at batch creation)
- Show queue summary:
```
@ -162,8 +162,7 @@ Queue Summary: [X] posts scheduled
- [Date] [Time]: "[hook preview]" — [pillar] ([format])
- [Date] [Time]: "[hook preview]" — [pillar] ([format])
View full schedule: /linkedin:calendar
Mark as published: /linkedin:publish
View full schedule + mark as published: /linkedin:calendar
Remember: Run 5x5x5 engagement 15 min before each post!
```

View file

@ -2,19 +2,26 @@
name: linkedin:calendar
description: |
View and manage your post scheduling queue. Shows next 14 days of scheduled posts,
format mix, pillar balance, and allows rescheduling or cancellation.
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".
"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
---
# LinkedIn Content Calendar
You are a LinkedIn content calendar manager. Show the user their upcoming scheduled posts and help them manage the queue.
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
@ -72,20 +79,91 @@ OVERDUE:
Use AskUserQuestion:
1. **Reschedule a post** — Move a post to a different date/time
2. **Cancel a post** — Remove from queue (set status to "cancelled")
3. **Mark as published** — Quick route to `/linkedin:publish`
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
### Reschedule Flow
### Action: Mark as Published
This is the publish flow (no separate command — runs inline here).
**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).
**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 after marking:
```
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
- Or use post-feedback-monitor agent for real-time tracking
```
**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 new date and time
3. Update queue.json via queue_update_status + queue_add with new date
4. Show updated calendar
### Cancel Flow
### Action: Cancel
If they choose to cancel:
1. Ask which post
2. Confirm cancellation
@ -94,7 +172,8 @@ If they choose to cancel:
node --input-type=module -e "import { queueUpdateStatus } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/queue-manager.mjs'; console.log(queueUpdateStatus('[post-id]', 'cancelled'));"
```
### View Draft Flow
### Action: View Draft
If they want to see a draft:
1. Ask which post
2. Read the draft file from the `draft_path`
@ -102,7 +181,7 @@ If they want to see a draft:
## Step 4: Balance Analysis
After showing the calendar, provide brief 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%.
@ -112,4 +191,5 @@ After showing the calendar, provide brief analysis:
## Reference Files
- `${CLAUDE_PLUGIN_ROOT}/references/scheduling-strategy.md`
- `${CLAUDE_PLUGIN_ROOT}/references/engagement-frameworks.md`
- `${CLAUDE_PLUGIN_ROOT}/assets/drafts/queue.json`

View file

@ -51,7 +51,7 @@ Upcoming Posts (next 7 days):
If there are overdue posts, display with warning:
```
OVERDUE (should have been posted):
[date]: "[hook preview]" — Run /linkedin:publish to update or /linkedin:calendar to reschedule
[date]: "[hook preview]" — Run /linkedin:calendar to mark as published or reschedule
```
If queue is empty: "No posts scheduled. Run /linkedin:batch to plan your week."
@ -80,8 +80,7 @@ Present these options to the user:
| `/linkedin:video` | Create video scripts with hook, body, CTA, captions, and thumbnail suggestions |
| `/linkedin:newsletter` | Long-form orchestrator — newsletter editions, essays, series articles (research → draft → fact-check → persona-review → lock → delivery). The single long-form entry point |
| `/linkedin:batch` | Create a full week of content in one session |
| `/linkedin:calendar` | View and manage your post scheduling queue |
| `/linkedin:publish` | Mark scheduled posts as published |
| `/linkedin:calendar` | View and manage your post scheduling queue + run the publish action (mark a scheduled post as published) |
### Strategy & Optimization
@ -164,7 +163,7 @@ If the user's intent is clear from context:
- Mentions "pipeline" or "end to end" → Route to `/linkedin:pipeline`
- Mentions "batch" or "week of content" → Route to `/linkedin:batch`
- Mentions "calendar" or "schedule" or "queue" or "upcoming posts" or "what's scheduled" → Route to `/linkedin:calendar`
- Mentions "publish" or "mark as published" or "posted today" or "just published" or "post is live" → Route to `/linkedin:publish`
- Mentions "publish" or "mark as published" or "posted today" or "just published" or "post is live" → Route to `/linkedin:calendar` (publish action)
- Mentions "plan" → Suggest `content-planner` agent
- Mentions "profile" or "360Brew" → Route to `/linkedin:profile`
- Mentions "not working" or "low reach" → Route to `/linkedin:analyze`

View file

@ -705,9 +705,10 @@ Next: Step 10 — Scheduling.
## Step 10: Scheduling — register the edition in the plugin queue
The locked, conversion-passed edition is ready to ship. Register it in the
plugin's native scheduling queue so it shows up in `/linkedin:calendar`,
`/linkedin:publish`, and the posting-time reminders — the same queue the
short-form pipeline uses. The edition is now a first-class scheduled post.
plugin's native scheduling queue so it shows up in `/linkedin:calendar`
(both for viewing and for the publish action) and the posting-time
reminders — the same queue the short-form pipeline uses. The edition is
now a first-class scheduled post.
**Procedure:**
@ -732,8 +733,8 @@ short-form pipeline uses. The edition is now a first-class scheduled post.
3. **Persist + close the edition.** Set the article's `status: "scheduled"`,
`scheduled: "<YYYY-MM-DD HH:MM>"`, and `currentPhase: "scheduling"` in
`edition-state.json`. Append a closing "edition scheduled → ready for
`/linkedin:publish` on <date>" pointer to the HANDOVER §6. The pipeline is
`edition-state.json`. Append a closing "edition scheduled → mark live via
`/linkedin:calendar` on <date>" pointer to the HANDOVER §6. The pipeline is
complete.
```
@ -741,7 +742,7 @@ Scheduling.
- Queue entry: <series-slug>-NN → assets/drafts/queue.json (status: scheduled)
- Slot: YYYY-MM-DD HH:MM format: newsletter
- Article status: scheduled
Edition complete. Visible in /linkedin:calendar; mark live with /linkedin:publish.
Edition complete. Visible in /linkedin:calendar; mark live via /linkedin:calendar (publish action).
```
---

View file

@ -1,115 +0,0 @@
---
name: linkedin:publish
description: |
Mark a scheduled post as published and update tracking state. Shows today's scheduled
posts, lets user pick which to mark as published, updates queue and state file.
Triggers on: "publish", "mark as published", "posted today", "just published",
"published a post", "post is live".
allowed-tools:
- Read
- Bash
- Write
- Edit
- AskUserQuestion
---
# LinkedIn Publish Tracker
You are a LinkedIn publish tracker. Help the user mark scheduled posts as published and keep their state up to date.
## Step 1: Load Today's Queue
```bash
node --input-type=module -e "
import { queueToday, queueOverdue, queueFormatSummary } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/queue-manager.mjs';
console.log('=== TODAY ===');
console.log(queueFormatSummary(queueToday()));
console.log('=== OVERDUE ===');
console.log(queueFormatSummary(queueOverdue()));
"
```
Also read state: `~/.claude/linkedin-thought-leadership.local.md`
## Step 2: 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 for today and none overdue:
```
No posts scheduled for today.
- Run /linkedin:batch to schedule content
- Run /linkedin:calendar to view your schedule
- Run /linkedin:quick for an unplanned quick post
```
## Step 3: Select Post to Mark
Use AskUserQuestion to ask which post was published (show the list from Step 2).
## Step 4: Mark as Published
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'));"
```
## Step 5: Update State File
Update state 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.
## Step 6: First-Hour Engagement Reminders
After marking as published, show the first-hour battle plan:
```
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
- Or use post-feedback-monitor agent for real-time tracking
```
## Step 7: Ask About More
Use AskUserQuestion:
1. **Mark another post** — I published more than one
2. **View calendar** — See remaining schedule → `/linkedin:calendar`
3. **Done** — All set for now
## Reference Files
- `${CLAUDE_PLUGIN_ROOT}/assets/drafts/queue.json`
- `${CLAUDE_PLUGIN_ROOT}/references/engagement-frameworks.md`