--- name: files-upload description: > Get images and video into Inklate media storage and ready to attach: upload local files (or reference public URLs), wait for processing, and hand back reusable file ids with their readiness. Covers jpg, jpeg, png, gif, and webp images plus mp4 and mov video. Use when the user says "upload these images", "get this video into Inklate", "attach this file to a post", or asks "is my media ready". Requires the Inklate CLI (npm i -g inklate) or the Inklate MCP connection — local files need the CLI or the local `inklate mcp` bridge; a remote MCP connection takes public https URLs instead. Reads social-context.md when present only to name the channels whose media rules matter. Upload once, reference the file id on as many posts as needed. license: MIT metadata: version: 0.1.1 topics: - media examplePrompt: "Upload these three product shots and give me ids I can attach" pairsWith: - social-carousel mcpTools: - files_upload - files_get - files_list - files_delete cliCommands: - files upload - files get - files list - files delete - mcp --- Move media into Inklate once, verify it processed, and return the file ids everything else attaches. ## Transports - **CLI (preferred):** `inklate files upload ` handles the whole handshake — presigned reservation, byte upload, and polling until each file leaves `processing`. YAML on stdout (`--json` for JSON), progress on stderr, and typed exit codes: `3` no credentials, `4` a deliberate refusal with its machine-readable payload on stdout, `6` the resource changed underneath you, `7` rate limited but safe to re-run later. Pass `--no-input` when running unattended, and `--yes` to carry an approval the user has already given in chat (deleting a file needs it). - **Local MCP bridge:** `inklate mcp` exposes `files_upload` taking local file paths (same handshake, run by the bridge). - **Remote MCP:** cannot read local disks — pass a public `https://` URL directly in `posts_create`'s `media` list instead; the server fetches and stores it (poll `files_get` until ready). ## 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-files-upload`) and STOP. 3. If multiple organizations exist, ask which one owns the media library (CLI: `--org `). ## Context Read `social-context.md` (project root, then `.agents/social-context.md`) if present, and use it for one thing only: `## Platforms` names the channels this media is headed for, which is what makes the handoff in step 5 specific ("these two are for the LinkedIn carousel") instead of generic. Uploading does not depend on it — proceed without it. ## Workflow 1. Sort the inputs: local paths versus URLs. Local files ride the upload; public https URLs skip it — they attach directly on a post and are ingested server-side. 2. Check the files fit before sending: supported types are jpg/jpeg/png/gif/webp (images, ≤25 MB) and mp4/mov (video, ≤512 MB), at most 10 per batch. Oversized or unsupported files are reported up front, not discovered as server errors. **`.webm` is not accepted for upload** — the server can store WebM but refuses it on ingest, because its prober can't extract the duration and dimension facts the per-provider gates need. Tell the user to transcode to mp4 rather than attempting the upload. 3. Upload with `inklate files upload ` (bridge: `files_upload` with `paths`). The command waits for processing by default and prints one `file_…` id per file. Pass an `--idempotency-key` when retrying a batch so a rerun replays instead of duplicating. 4. Verify readiness in the output: `ready` files are attachable; `failed` files carry a machine-readable reason (`too_large`, `unsupported_type`, `content_mismatch`, …) — report the reason verbatim and what would fix it. `inklate files get --url` fetches a signed preview URL when the user wants to eyeball the stored asset. 5. Hand off: list the file ids in attach order and the exact usage — `--media file_… --media file_…` on `inklate posts create`, or the `media` array on `posts_create`. Channel media rules (counts, required media on Instagram) are enforced in that create (or `posts validate`) response's per-channel `errors`/`warnings` now, not a separate check — mention them only when the user names the target channels. 6. For library questions ("what have I uploaded?"), use `inklate files list` (MCP: `files_list`) with its readiness/kind filters, newest first. ## Quality bar - Never invent a file id: every id in the receipt came from this session's upload or list output. - Failed uploads are reported with the server's reason verbatim — no softening, no retry loops past two attempts. - Local paths are echoed next to their file ids so the user can map files to ids at a glance. - This skill stores and reports; attaching to posts is `posts-create`'s job. Deleting is permanent, so `inklate files delete --yes` runs only on an explicit request — and it is refused outright while the file is attached to a post, which is a reason to report, not a lock to work around. ## Receipt End with: | File | File ID | Kind | Readiness | Notes | | ------------- | ----------- | ----- | --------- | ---------------- | | ./hero.png | file_a1b2c3 | image | ready | — | | ./demo.mov | file_d4e5f6 | video | ready | 42 MB | | ./massive.mov | — | video | refused | too_large (>512) | Below the table: the attach-order id list ready to paste (`--media file_a1b2c3 --media file_d4e5f6`), and any failed files with their fix.