--- name: posts-create description: > Create a post on Inklate in one call — a draft, a scheduled post, or a publish-now, decided entirely by whether you pass a schedule time — with the server's per-channel validation riding along in the same response instead of a separate check. Covers LinkedIn, X (Twitter), Instagram, and Facebook today, with Mastodon, Threads, Bluesky, TikTok, and YouTube in platform review. Use when the user says "draft this on Inklate", "create a post", "get this ready to publish", or "post this now". Requires the Inklate CLI (npm i -g inklate) or the Inklate MCP connection. Reads social-context.md when present so drafts and autofixes stay on-voice. A draft with errors still saves — arming it with a schedule or publish time is the only thing the server refuses, and that refusal never leaves a partial write behind. license: MIT metadata: version: 0.2.0 topics: - drafting - checking examplePrompt: "Draft this announcement on Inklate for LinkedIn and X and tell me if it's ready" pairsWith: - social-post - social-thread - social-crosspost mcpTools: - posts_create - posts_update - files_upload cliCommands: - posts create - posts update - files upload --- Create a draft on real channels in one call and hand back the server's own verdict — the safe half of the loop, ending exactly where a human (or the posts-publish skill) takes over. ## 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 not-ready post, or a plan limit — 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. This skill never passes `--scheduled-at` on `posts create` — that turns the call into an arming attempt, which is `posts-publish`'s job. - **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-posts-create`) and STOP. 3. If multiple organizations exist, ask which one (CLI: `--org `). ## Craft boundary This skill owns create → read the verdict → fix judgment calls. The WRITING belongs to the paired craft skills: when `social-post`, `social-thread`, or `social-crosspost` is installed, get the draft from them first and feed their output in. Without them, use the user's text as given and make only the edits the server's response demands. ## Context Read `social-context.md` (project root, then `.agents/social-context.md`) if present. `## Voice` and `## Never` constrain any fix that rewrites text; `## Platforms` resolves vague targets ("post this" → the user's primary channel). Proceed without it if absent. ## Workflow 1. Establish content and targets. Markdown is the input sugar: a `---` line splits thread items on channels that thread. Confirm target channels; provider names ("x", "linkedin") resolve server-side when the organization has one channel per platform. 2. If the post needs media from local files, upload them first with `files_upload` (CLI: `inklate files upload ./photo.jpg` — prints reusable `file_…` ids; the CLI also accepts local paths directly on `--media` and uploads them in-line). Media already on the web passes as a public https URL. 3. **Create the draft in one call.** `inklate posts create --content … --channel … --media …` (MCP: `posts_create` with `{content, channels, media}`; `autofix` defaults to `true`). Never pass `--scheduled-at` here. The response IS the verdict — no separate check to run: a top-level `ready`, and per channel a `content.text` (the actual copy that channel will receive), `errors[]` (`code`, `field`, a human message with the real numbers, and a machine `fix` where one exists), and `warnings[]`. A draft with errors still saves fine — exit 0, the errors ride along on stdout; only arming a schedule or publish time is refused. Add `--idempotency-key ` when a create might be retried — a rerun replays instead of leaving two drafts of the same post behind. 4. **Autofix already ran before you saw the response.** Default on, it silently repairs what it can — `trim_to_fold`, `rebalance_thread`, `set_title` — and reports every repair in `fixes_applied` (channel + kind; no extra call needed). Read out every entry as "channel — kind (auto-applied)" so nothing rewrites the user's words without them knowing. Pass `--no-autofix` (`autofix: false`) when the user wants to see the raw violations before anything is touched. 5. For every error still remaining (autofix had nothing to offer, or was off), the message already names the real numbers — no code-to-English translation needed. If `errors[].fixes` offers a repair you want to apply, call `inklate posts update --per-channel '{"": {"content": "…"}}'` (MCP: `posts_update`) with the corrected copy for just that channel; the response re-validates and returns the same shape, so there is no separate re-check step. When a fix would change meaning or trips `## Never`, treat it as a judgment call instead — 2–3 concrete options to the user, not a guess. 6. **Stop once the draft matches the user's intent — ready or not.** `ready` gates arming, never saving. Publishing and scheduling are the `posts-publish` skill's job (or the user's, in the dashboard): offer the next command (`inklate posts publish post_abc123 --wait`) but never run it from this skill. A stand-alone dry run against a stored post or inline text with nothing written is a handoff to `inklate posts validate` — mention it, don't run it here. ## Quality bar - `ready` is the only publishability signal to trust — never infer it from the absence of one particular error code. - Every entry in `fixes_applied` is named to the user by channel and kind; a silent autofix is a bug. - The receipt's post ID is the contract: any later skill or command can pick the draft up from it. - This skill never passes `--scheduled-at` / a `scheduled_at` value — arming is `posts-publish`'s call, even when the draft is already green. - A draft with errors is not a failure to hide: report it plainly, exit 0, and move straight to the fixes. - Judgment-call fixes are applied through `posts update`, never invented ahead of what the response's `fixes` array actually offered. - This skill never publishes, schedules, or deletes — its output is a validated draft. Editing or cancelling an existing post is `posts-update`'s job. ## Receipt End with: | Post ID | Channel | Ready | Autofix applied | Remaining error | | ----------- | --------- | ----- | ---------------- | ---------------------------------- | | post_abc123 | LinkedIn | yes | — | — | | post_abc123 | X | yes | rebalance_thread | — | | post_abc123 | Instagram | no | — | media required (no image attached) | Below the table: whether the draft as a whole is `ready` (or which channels are parked and why), the exact next command to ship it (`inklate posts publish post_abc123 --wait` or the posts-publish skill), and any media file ids attached.