Post content
Skill NovateStudioGit/novate-studio-skills/creative-production/post-content
Single-command video distribution. Locates a video on disk, transcribes the audio with Whisper, writes a platform-appropriate caption in the matching creator/brand voice, picks a cover, queues it to Buffer, and schedules to IG Reels + TikTok + YouTube Shorts (LinkedIn/Threads optional). Use this skill whenever the user wants to publish, post, ship, schedule, queue, or cross-post a video to social — including phrases like "post that video", "ship the latest reel", "send to Buffer", "queue for IG", "post to shorts", "publish the Example Creator clip", or any natural-language post request that names a video file, a Downloads target, or a creator/brand folder. Also trigger when the user mentions a specific reel/short by name and asks for it to go live anywhere.From its SKILL.md
npx -y skills add NovateStudioGit/novate-studio-skills --skill post-contentAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.5 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
/post-content
One command, raw video in → scheduled posts out. The model drives — the scripts do the deterministic parts (find file, transcribe, upload). Will approves before anything ships to Buffer.
Flow
- Parse intent — pull from the user's prompt: which video, which channels, when to post, any caption hints.
- Locate the video —
scripts/find_video.pywith the intent. - Transcribe —
scripts/transcribe.py(local whisper.cpp — no API key, runs on-device. Cached next to the video as.transcript.txt). - Detect voice — from the video's path (see voice map below). Read the matching persona doc(s) before writing.
- Draft caption + hashtags — in that voice, max 5 hashtags.
- Pick cover —
scripts/extract_cover.py. Default: frame at 0.5s. If/viralitywas run on this video, pull the peak-hook frame instead. Offer/thumbnailfor a designed cover. - Approval gate — show Will the caption, cover, channels, scheduled time. Wait for
ship,tweak X, orcancel. Never auto-post. - Publish —
scripts/buffer_post.pyfans out to the selected Buffer channels. - Log — write a one-line confirmation note to the relevant Obsidian project's
sessions/YYYY-MM-DD.md.
Voice detection — read the persona docs, don't infer
The video's parent path determines voice. Always read the persona doc before drafting — caption voice is too easy to get wrong from filename guessing alone.
| Path pattern | Voice source |
|---|---|
STUDIO/1-WillSchwx.io/** | Will's locked voice — see references/voice-will.md AND ~/notes/your-brand/index.md |
STUDIO/[creator]/** (other) | ~/notes/[creator-slug]/ persona/voice docs |
ECOMMERCE/[brand]/** | ~/notes/[brand-slug]/ brand voice docs |
~/Downloads/** or anywhere else | Ask Will which creator/brand this is for — don't guess |
For Will: typographic discipline (command form, parallel structure, one idea per line, memorable metaphor allowed). For Example Creator specifically, the /caption-IG-Example Creator skill already encodes the voice — invoke it instead of recreating it.
Hashtag rule — locked, never override
Max 5 hashtags per caption. Compose them as: 1 niche + 1 broad + 2 thesis + 1 shareability. Never more, regardless of platform best-practice claims. (Will's standing rule.)
Never fabricate
Captions may only reference what's in the transcript or what Will provided in this turn. No invented stats, names, dates, or claims. If a number/quote feels persuasive but isn't sourced, leave it out — don't fill the gap.
Channels — defaults and flags
Default: IG Reels + TikTok + YouTube Shorts.
+linkedin/+threadsto add.only ig/only tiktok/only shortsto restrict.- LinkedIn and Threads use rephrased copy (LinkedIn = pro framing + first-person; Threads = conversational, no hashtags).
Timing
- No timing said → immediate post.
queue→ next available Buffer slot.at 7pm/tomorrow 9am→ specific time (interpret in Will's local tz; today's date is in context).
Cover
- If
~/Desktop/STUDIO/**/virality/[video-stem]/score.jsonexists → pullpeak_hook_timestampand grab that frame viaextract_cover.py --at <ts>. - Else → grab
--at 0.5. - Always offer: "Want me to run /thumbnail for a designed cover instead?" — if Will says yes, invoke
/thumbnailand use that output.
Approval gate format
Show this exact structure before calling Buffer:
Ready to ship:
Video: <basename> (<duration>s, <w>x<h>)
Cover: <path> ← reply "thumbnail" to upgrade
Channels: <ig/tiktok/shorts/...>
Time: <immediate | YYYY-MM-DD HH:MM tz | "next queue slot">
Caption (IG/TikTok primary):
<caption>
#tag1 #tag2 #tag3 #tag4 #tag5
LinkedIn (if selected):
<rephrased copy>
Reply: ship | tweak <what> | cancel
Script contracts
All scripts live in scripts/ and read flags from CLI. None of them call Buffer or Whisper unless invoked — safe to dry-run.
find_video.py <intent-string>→ prints absolute path of the resolved video. Resolution order: 1) explicit path in intent, 2) newest.mp4/.mov/.mkvin~/Downloads(≤ 7 days old), 3) newest in~/Desktop/STUDIO/**/content/, 4) newest in~/Desktop/ECOMMERCE/**/content/. Errors clearly if ambiguous.transcribe.py <video-path>→ writes<video>.transcript.txtand prints the transcript. Skips if cache exists. Uses local whisper.cpp; auto-downloads the small.en model on first run.extract_cover.py <video-path> [--at <seconds>]→ writes<video>.cover.jpgnext to the video.buffer_post.py --video <path> --cover <path> --caption-file <path> --channels ig,tiktok,shorts --when <immediate|ISO8601>→ posts via Buffer's GraphQL API atapi.buffer.com. RequiresBUFFER_ACCESS_TOKENand~/.claude/skills/post-content/config.jsonshaped{"organization_id": "...", "channels": {"ig": "<channelId>", "tiktok": "<channelId>", "shorts": "<channelId>"}}. Buffer's API has no upload endpoint, so the script either spins up acloudflaredtunnel automatically (default) or accepts--public-url-base <https-url>if you host the files yourself.
Run pip install -r ~/.claude/skills/post-content/scripts/requirements.txt the first time.
First-time setup (only if not done)
~/.claude/skills/post-content/config.jsonmust exist withorganization_id+channelsmap. If missing, runpython scripts/buffer_post.py --write-configonce (after channels are connected in Buffer) — it auto-detects the org and maps services to keys (ig,tiktok,shorts,linkedin,threads).BUFFER_ACCESS_TOKENmust be exported (check~/.zshrcor project.env). If missing, stop and tell Will exactly what to set and where.whisper-cppmust be on PATH —brew install whisper-cppif missing. (No API key, no per-minute cost — runs locally.)cloudflaredmust be on PATH —brew install cloudflaredif missing. Used to expose the local video file to Buffer's servers for the duration of the post-creation call. Skip if passing--public-url-base(you host the files yourself).
Composes with
/virality— run BEFORE posting to score the hook; reuse itspeak_hook_timestampfor cover./thumbnail— designed cover overlay; use when Will accepts the upgrade./caption-IG-Example Creator— Example Creator captions live there; defer to it instead of duplicating voice rules./vysionary-carousel— Will's @yourbrand.ai posts that are carousels, not reels — route those over there instead.
When NOT to use this skill
- Carousels / static image posts → use
/vysionary-carouselor post manually. - Editing an already-scheduled Buffer post → ask Will to do it in Buffer's UI; this skill only creates new posts.
- Stories / live streams → out of scope.
What ships with it: 7 files
26.5 KB alongside SKILL.md, 4 of them executable
references/
- buffer-setup.md2.3 KB
- voice-will.md1.2 KB
scripts/
- buffer_post.pyruns14.5 KB
- extract_cover.pyruns1.1 KB
- find_video.pyruns3.7 KB
- requirements.txt16 B
- transcribe.pyruns3.7 KB