Inklate skill · Plan

calendar-schedule

Turn a content plan — often produced by the social-calendar or social-repurpose skills — into real scheduled posts on the real calendar: parse the plan, map…

frontmatter · SKILL.md+
name: calendar-schedule
description: >
  Turn a content plan — often produced by the social-calendar or social-repurpose skills —
  into real scheduled posts on the real calendar: parse the plan, map every row to a
  connected channel, preview each row's server-side readiness, propose the full week's
  schedule, and book every row with one call each. Covers LinkedIn, X (Twitter),
  Instagram, and Facebook today, with Mastodon, Threads, Bluesky, TikTok, and YouTube in
  platform review. Use when the user says "schedule my week", "schedule this plan", "put
  my calendar on the calendar", or "schedule these posts". Requires the Inklate CLI
  (npm i -g inklate) or the Inklate MCP connection. Reads social-context.md when present
  for cadence and voice. The receipt is the week as a table of real scheduled times and
  post IDs, verified against the queue.
license: MIT
metadata:
  version: 0.2.0
  topics:
    - planning
    - publishing
  examplePrompt: "Schedule this two-week content plan across my channels"
  pairsWith:
    - social-calendar
    - social-repurpose
  mcpTools:
    - posts_create
    - posts_validate
    - posts_list
    - posts_get
  cliCommands:
    - posts create
    - posts validate
    - posts list
    - posts get

Take a content plan off the page and onto the real calendar: every row becomes one validated, scheduled post with a confirmed time on a real channel.

Transports

  • CLI (preferred): inklate (npm i -g inklate) — structured YAML on stdout (--json for JSON), progress on stderr, and typed exit codes: 3 no credentials, 4 a deliberate refusal — arming a row that isn't ready — with its machine-readable payload on stdout, 5 not found, 6 the resource changed underneath you, 7 rate limited but safe to re-run later. Pass --no-input when running unattended so a missing flag fails loudly instead of prompting, and --yes to carry an approval the user has already given in chat.
  • MCP (fallback): the Inklate MCP tools when the CLI is not installed.

Steps below name the MCP operation with the CLI form in parentheses; the fields are the same on both.

Connection check

  1. Run inklate auth check (MCP: organizations_list) first.
  2. On exit 3 (no credentials): interactively run inklate auth login; headless, the key comes from INKLATE_API_KEY=inklate_… in the environment or inklate auth set --token inklate_…. If neither transport is available at all, tell the user to install with npm i -g inklate (or connect at https://inklate.com/skills/connect?ref=skills-calendar-schedule) and STOP.
  3. If multiple organizations exist, ask which one owns this calendar. All times and channels below are scoped to that organization (CLI: --org <slug>).

Context

Read social-context.md (project root, then .agents/social-context.md) if present. ## Cadence is the reality check against overstuffed plans, ## Platforms resolves ambiguous channel names in the plan, and ## Voice / ## Never govern any content adjustments you make while resolving errors. Proceed without it if absent.

Workflow

  1. Parse the plan. Read the plan table or list the user provides. Extract per row: content (or a pointer to a draft), target channel(s), and intended day/time. Flag rows you cannot parse instead of guessing.
  2. Map rows to channels. Match each row's platform to a connected channel. Rows targeting unconnected platforms get flagged and set aside — never silently dropped, never scheduled to a wrong channel.
  3. Preview every row before booking anything. Call posts_validate (CLI: inklate posts validate --content … --channel …) for each mapped row — a dry run, nothing is stored. Read each row's ready and per-channel errors[]/warnings[]. For every fixable error, adjust the row's content directly (autofix already applied where it could when you actually create it, but a dry-run preview surfaces judgment calls early); surface judgment calls to the user in one consolidated batch, not one interruption per row.
  4. Build the proposed schedule. Convert the plan's intended times into concrete datetimes in the organization's timezone, then apply the constraints:
    • Spacing: no two posts on the same channel within 3 hours — when the plan violates this, nudge the later post and note the nudge.
    • Existing queue: check posts_list (CLI: inklate posts list --scheduled-from … --scheduled-to …) for already-scheduled posts in the window so the new week doesn't collide with them.
    • Rows with a day but no time get a sensible default for that platform, marked as your suggestion.
    • Past times are an error, not a nudge: flag any row whose intended time has already passed.
  5. Approval gate. Present the entire proposed week as one table — day, time (org timezone), channel, content summary, readiness from step 3 — before anything is booked. Scheduling posts is a real-world action: get an explicit yes on the whole table, or apply the user's edits and re-present. Never book a partial week on an implicit go-ahead.
  6. Book it — one call per row. On approval, call posts_create (CLI: inklate posts create --content … --channel … --scheduled-at <time> --idempotency-key <row-id> --yes) for each row; the same call both writes the post and arms it for that instant, so there is no separate sync, preflight, or schedule-placements step left to run. The --idempotency-key (the plan row's own identity is a good source) makes a retry replay instead of creating a second copy of the same row.
    • If a row's arming is refused (exit 4 — its content stopped being ready since the preview, e.g. a channel disconnected in the meantime), park that row: retry the identical call without --scheduled-at to at least save it as a draft, note the reason, and keep going with the rest of the batch.
  7. Verify. Call posts_list for the scheduled window and confirm every booked row appears with the right time and channel. For any row still processing, call posts_get (CLI: inklate posts get <id>) and report the real state — there is no separate status call. Discrepancies between what was booked and what the queue shows are reported immediately, not glossed.
  8. Handle a mid-batch stop honestly. Booking a whole week is many calls, so expect to be interrupted:
    • Exit 7 (rate limited) means the budget is spent and the message names the wait. Report which rows are booked, which are not, and the wait — then offer to resume. Never report a half-booked week as done, and never abandon it as failed.
    • Exit 6 (conflict) means that post's state forbids the booking (already published, channel disconnected). Park that row with the reason and keep going with the rest.
    • Rerunning a row that already succeeded is safe when it carries the same idempotency key from step 6.
  9. If the plan spans more than one week, book week by week — approve and verify week one before proposing week two, so a mid-plan change of mind never strands half-booked rows.

Quality bar

  • Never book a row that previewed as not ready; parked rows appear in the receipt as unscheduled with the reason.
  • All times are stated and booked in the organization's timezone; if the plan's times imply a different timezone, resolve the conflict with the user before proposing.
  • Spacing rule is hard: no two posts on one channel within 3 hours. Nudges made to satisfy it are listed explicitly.
  • The proposed-schedule table shown for approval must match what was actually booked — no drift between preview and booking.
  • Verification via posts_list is not optional; the receipt reflects the queue's reality, not the request you sent.
  • If the plan exceeds the cadence in social-context.md by more than double, say so once before proposing — then respect the user's call.
  • Every row in the receipt carries its post ID so the user can edit or unschedule any single slot later without archaeology.

Receipt

End with the booked week as verified against the queue:

Day Time (org tz) Channel Post ID Content Status
Mon Jul 20 9:00 AM LinkedIn post_abc123 "Why we killed our roadmap…" scheduled
Mon Jul 20 1:00 PM X post_abc124 Thread: roadmap lessons (6 tweets) scheduled
Wed Jul 22 10:00 AM Instagram post_abc125 Carousel: 5 lessons processing (media pending)

Below the table: total posts booked, any rows parked with reasons, any spacing nudges applied, and the posts_get state for anything not yet confirmed on the calendar.

Every row in this table was verified against posts_list — it is the queue's account of the week, not the request's.

Related skills