docs(skills): add the release announcement kit

Add the release-promo skill: the X post in the house format, its reply
with the release links, a one-sentence quote line for the second
account, and the promo video rendered from the store captures, with a
helper for X's weighted length and math-bold versions. The desktop and
mobile release flows now end with it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
DIYgod
2026-10-03 00:35:48 +08:00
parent cdd3942d3d
commit 8de170d371
7 changed files with 244 additions and 6 deletions
+8 -2
View File
@@ -1,6 +1,6 @@
---
name: desktop-release
description: Full Folo desktop release from dev to the stores. Drafts the changelog, picks the build or ota mode, bumps the version and opens the release PR, verifies the packages GitHub Actions builds for the PR and every change in the release, merges the PR, watches the tag and store builds, gets the Windows installer signed, publishes the GitHub release, and submits the Mac App Store and Microsoft Store builds for review. Use when the user asks to release, ship or publish the desktop app.
description: Full Folo desktop release from dev to the stores. Drafts the changelog, picks the build or ota mode, bumps the version and opens the release PR, verifies the packages GitHub Actions builds for the PR and every change in the release, merges the PR, watches the tag and store builds, gets the Windows installer signed, publishes the GitHub release, submits the Mac App Store and Microsoft Store builds for review, and drafts the announcement post and promo video. Use when the user asks to release, ship or publish the desktop app.
disable-model-invocation: true
argument-hint: "[optional notes, e.g. a preferred mode or the OTA runtime]"
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
@@ -10,7 +10,7 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
The release flow shared by every agent. Claude Code reaches it through `.claude/skills/desktop-release/SKILL.md`, which maps these steps onto Claude's tools. Change the flow here, not in the adapter.
Phase A prepares the release, Phase B verifies the packages GitHub Actions builds for the PR, and Phase C merges and carries the release through every store. The run ends only when every item in **Definition of done** is checked, or when the only open items are blocked on the user and you have told them exactly what is left.
Phase A prepares the release, Phase B verifies the packages GitHub Actions builds for the PR, and Phase C merges, carries the release through every store and drafts its announcement. The run ends only when every item in **Definition of done** is checked, or when the only open items are blocked on the user and you have told them exactly what is left.
## Standing authorization
@@ -37,6 +37,7 @@ Still ask first: code changes that fix something found during verification; acce
- [ ] Microsoft Store: new submission with `Folo.appx` `<version>.0` and What's New, submitted for certification
- [ ] `chore(sync): merge main into dev` merged, or its blocker reported
- [ ] Verification report and final summary delivered; temporary apps, profiles, mounts and browser tabs cleaned up
- [ ] Announcement kit delivered: X post, reply with the release links, quote line and promo video (C10)
## How to work
@@ -300,6 +301,10 @@ Tell the user: version, mode and `runtimeVersion`; PR and merge commit; tag; Git
Clean up: quit the test app, restore `~/.folo/config.json` if it changed, unregister and delete app copies, profile copies and mounts, delete the downloads (keep the report and screenshots), close the browser tabs you opened, and release any granted credentials.
### C10. Announcement kit
Follow `.agents/skills/release-promo/SKILL.md`: pick the highlights, draft the X post, its reply with the release links and a one-sentence quote line for the second account, and render the promo video from the store captures. If a mobile release runs in the same session, make one kit for both after the later release's report. These are drafts; the user posts them.
## Failure handling
- The bump fails halfway: nbump restores `package.json` only when a trailing hook fails and rolls nothing back after a leading hook fails. Check `git status` and `git branch --list 'release/desktop/*'`, restore the tree with `git restore --staged --worktree apps/desktop` and delete the generated `apps/desktop/changelog/<version>.md` if the bump renamed `next.md`. Ask before deleting a leftover local release branch. If the branch reached GitHub but `gh pr create` failed, open the PR by hand with the title and body from `bump.config.ts`.
@@ -322,4 +327,5 @@ Clean up: quit the test app, restore `~/.folo/config.json` if it changed, unregi
- OTA metadata builder: `.github/scripts/build-ota-release.mjs`; Mac App Store upload: `.github/scripts/upload-mas-pkg.sh`
- Workflows: `.github/workflows/build-desktop.yml`, `.github/workflows/tag.yml`, `.github/workflows/sync.yaml`
- OTA worker: `apps/ota` (its `README.md` lists manifest and policy checks); desktop updater: `apps/desktop/layer/main/src/updater/`
- Announcement kit: `.agents/skills/release-promo/SKILL.md`; promo video renderer: `store-assets/scripts/release-video.ts`
- `references/verify-macos-build.md`, `references/store-submissions.md`, `references/release-notes.md`, `references/browser-and-credentials.md`, `scripts/pe-signature.mjs`
+9 -2
View File
@@ -1,6 +1,6 @@
---
name: mobile-release
description: Full Folo mobile release from dev to the stores. Decides between a store release and an OTA-only release, drafts the changelog, bumps the version and opens the release PR to mobile-main, verifies the PR's CI-built Android package and the iOS build on a simulator along with every change in the release, merges the PR, watches the build or OTA workflows, publishes the GitHub release, and submits the iOS build for App Store review and the Android build for Google Play review. Use when the user asks to release, ship or publish the mobile app.
description: Full Folo mobile release from dev to the stores. Decides between a store release and an OTA-only release, drafts the changelog, bumps the version and opens the release PR to mobile-main, verifies the PR's CI-built Android package and the iOS build on a simulator along with every change in the release, merges the PR, watches the build or OTA workflows, publishes the GitHub release, submits the iOS build for App Store review and the Android build for Google Play review, and drafts the announcement post and promo video. Use when the user asks to release, ship or publish the mobile app.
disable-model-invocation: true
argument-hint: "[optional notes, e.g. 'ota' or 'minor bump']"
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
@@ -10,7 +10,7 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep
The release flow shared by every agent. Claude Code reaches it through `.claude/skills/mobile-release/SKILL.md`, which maps these steps onto Claude's tools. Change the flow here, not in the adapter.
Phase A prepares the release, Phase B verifies the packages GitHub Actions builds for the PR, and Phase C merges and carries the release through every store. The run ends only when every item in **Definition of done** for the chosen mode is checked, or when the only open items are blocked on the user and you have told them exactly what is left.
Phase A prepares the release, Phase B verifies the packages GitHub Actions builds for the PR, and Phase C merges, carries the release through every store and drafts its announcement. The run ends only when every item in **Definition of done** for the chosen mode is checked, or when the only open items are blocked on the user and you have told them exactly what is left.
## Standing authorization
@@ -38,6 +38,7 @@ Store mode (`mode: store`):
- [ ] Android: Google Play production release with the new app bundle and release notes, sent for review
- [ ] `chore(sync): merge mobile-main into dev` merged, or its blocker reported
- [ ] Verification report and final summary delivered; temporary devices, downloads and browser tabs cleaned up
- [ ] Announcement kit delivered: X post, reply with the release links, quote line and promo video (C9)
OTA mode (`mode: ota`):
@@ -46,6 +47,7 @@ OTA mode (`mode: ota`):
- [ ] A device running the store binary for that runtime picks up the update (Settings → About shows `OTA <new version>`)
- [ ] GitHub release `Mobile v<version>` with `ota-release.json`, `dist.tar.zst` and `build.apk`: not a draft, not a prerelease, not latest
- [ ] Sync PR merged or reported; report delivered; cleanup done
- [ ] Announcement kit delivered (C9)
## How to work
@@ -306,6 +308,10 @@ Tell the user: version, mode, runtime and channel; PR and merge commit; tag; Git
Clean up: delete temporary simulators and emulators, shut down QA devices you booted, delete downloads (keep the report and screenshots), revert stray changes such as `Info.plist` build numbers from local builds, close the browser tabs you opened, and release any granted credentials.
### C9. Announcement kit
Follow `.agents/skills/release-promo/SKILL.md`: pick the highlights, draft the X post, its reply with the release links and a one-sentence quote line for the second account, and render the promo video from the store captures. If a desktop release runs in the same session, make one kit for both after the later release's report. These are drafts; the user posts them.
## Failure handling
- The bump fails halfway: nbump restores `package.json` only when a trailing hook fails and rolls nothing back after a leading hook fails. Check `git status` and `git branch --list 'release/mobile/*'`, restore the tree with `git restore --staged --worktree apps/mobile` and delete the generated `apps/mobile/changelog/<version>.md` if the bump renamed `next.md`. Ask before deleting a leftover local release branch. If the branch reached GitHub but `gh pr create` failed, open the PR by hand with the title and body from `bump.config.ts`.
@@ -329,4 +335,5 @@ Clean up: delete temporary simulators and emulators, shut down QA devices you bo
- Workflows: `.github/workflows/tag.yml`, `build-ios.yml`, `build-android.yml`, `publish-ota.yml`, `sync.yaml`, `lint.yml`, `build-web.yml`
- Build profiles and submit config: `apps/mobile/eas.json`; app config: `apps/mobile/app.config.base.ts`; iOS Info.plist: `apps/mobile/ios/Folo/Info.plist`
- OTA worker: `apps/ota` (its `README.md` lists manifest, policy and rollback steps)
- Announcement kit: `.agents/skills/release-promo/SKILL.md`; promo video renderer: `store-assets/scripts/release-video.ts`
- `references/verify-android-build.md`, `references/verify-ios-build.md`, `references/store-submissions.md`, `references/release-notes.md`, `references/browser-and-credentials.md`
+145
View File
@@ -0,0 +1,145 @@
---
name: release-promo
description: Announcement kit for a Folo release - the X post with the highlights, its reply with the release links, a one-sentence quote line for the second account, and a 1080p promo video rendered from real app captures. The desktop and mobile release skills end with it; also use it when the user asks for promo material for a release.
argument-hint: "[releases, e.g. 'desktop 1.15.0 mobile 0.5.11'; defaults to the releases this session shipped]"
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
---
# Release Promo
The announcement kit for a Folo release, shared by every agent. Claude Code reaches it through `.claude/skills/release-promo/SKILL.md`, which maps these steps onto Claude's tools. The desktop and mobile release skills run it as their last step. Change the flow here, not in the adapter.
The kit has four parts:
- **Post**: the announcement on X in the house format, with the video attached.
- **Reply**: the release links, posted as the first reply to the post.
- **Quote line**: one sentence the second (matrix) account adds when it quote-posts the announcement.
- **Video**: 20–30 seconds at 1920 × 1080, rendered from real app captures by `store-assets/scripts/release-video.ts`.
Everything here is a draft for the user. Never post, schedule or upload anything to X or another account; the user publishes the kit.
## Inputs
- Releases: the ones this session shipped. When desktop and mobile both ship in one session, make one kit for both after the later release's report. Outside a release run, cover the releases the user names; if they name none, ask, offering the newest tags (`git tag --sort=-creatordate | grep -E '^(desktop|mobile)/v' | head -4`).
- Changelogs: `apps/desktop/changelog/<version>.md`, `apps/mobile/changelog/<version>.md`.
- The releases' verification reports, when there are any. Headline only what shipped and passed; an item that was not run or only checked statically is no highlight.
- Working directory: `$SCRATCH/promo` in the release's scratch space, or a temporary directory.
## 1. Pick the highlights
Choose three or four changes across the releases, the most noticeable first. Each becomes one line of the post and one scene of the video, in the same order.
- Say what users get, not how it was built: "Settings sync across devices again", not "PATCH /settings covers non-local keys".
- Merge entries that belong together (settings sync and action language sync are one highlight).
- The last highlight may bundle a platform upgrade with the fixes ("Desktop on Electron 44, plus fixes").
- Name languages in their own script (中文, 日本語, Français). No competitor names, rankings or prices.
## 2. Post
Every announcement has the same shape. The user wrote the first one; follow it:
```text
Folo update time 💫 desktop 𝐯𝟏.𝟏𝟒.𝟎 and mobile 𝐯𝟎.𝟓.𝟏𝟎 are here!
Highlights:
🔄 Incremental sync, fewer requests
⚡ Instant read & star marks, even offline
📱 Reading settings, dark mode, immersive reading
🌐 Desktop follows your system proxy
👇 Full details below
```
```text
Folo update time 💫 desktop 𝐯𝟏.𝟏𝟓.𝟎 and mobile 𝐯𝟎.𝟓.𝟏𝟏 are here!
Highlights:
🔁 Settings sync across devices again
🎧 Steadier text-to-speech on long reads
🌍 Fully translated into 中文, 日本語 & Français
⚡ Desktop on Electron 44, plus fixes
👇 Full details below
```
- Keep the first line, "Highlights:" and "👇 Full details below" as they are. For one app: "Folo update time 💫 desktop 𝐯𝟏.𝟏𝟓.𝟎 is here!".
- Versions are in math bold: `node .agents/skills/release-promo/scripts/x-post.mjs bold 1.15.0`.
- One emoji per highlight, each different and matching its topic, neither 💫 nor 👇.
- No links (they go in the reply), hashtags or mentions.
- At most 280 by X's weighting, where CJK, emoji and math bold count twice; both posts above weigh 276. Shorten the highlights rather than drop parts of the format. Check every draft:
```bash
node .agents/skills/release-promo/scripts/x-post.mjs count post.txt reply.txt quote-en.txt quote-zh.txt
```
## 3. Reply
```text
Desktop v1.15.0: github.com/RSSNext/Folo/releases/tag/desktop/v1.15.0
Mobile v0.5.11: github.com/RSSNext/Folo/releases/tag/mobile/v0.5.11
Download: folo.is
```
One line per release with the plain version, then the download link. Every linked release must be published (`gh release view <tag> --json isDraft`).
## 4. Quote line
One sentence summing up the release, in English and in Simplified Chinese; the user picks. No version numbers, links, hashtags or emoji: the quoted post carries those.
```text
New Folo is out: your settings follow you to every device again, long reads get text-to-speech that holds up, and every screen now speaks Chinese, Japanese and French.
```
```text
Folo 新版上线:设置重新跨设备同步,长文朗读更稳,界面完整支持简繁中文、日文和法文。
```
## 5. Video
### Captures
The video shows real app screens only: the store screenshot captures, `store-assets/captures/<locale>/<device>/<scene>.png` (`store-assets/README.md`). They are not committed, so find the checkout that holds them and use the newest set:
```bash
ROOT=$(dirname "$(git rev-parse --path-format=absolute --git-common-dir)")
find "$ROOT/store-assets" "$ROOT/.claude/worktrees" -maxdepth 3 -type d -path '*store-assets/captures' 2>/dev/null
```
`ls <captures>/en/*` lists the shots. `listen` and `summary` phone shots come with a `.json` region (the player bar, the AI summary card) that a callout can enlarge.
A capture shows the app as it was when it was taken. The script prints each shot's capture time; compare it with the commits behind a highlight (`git log -1 --date=format-local:'%Y-%m-%d %H:%M' --format=%cd <sha>`) and never show a screen captured before its change landed. The `fr` captures of 2026-10-01, for example, still show the English title case b7df9ad01 removed. A highlight without a fitting capture gets a neutral screen (`hero`, `timeline`) and leaves the message to the copy. New captures change the shared demo account's subscriptions and language, so ask the user before taking them.
### Spec
Write `$SCRATCH/promo/video.json`, starting from `store-assets/promo/example.json` (the desktop 1.15.0 / mobile 0.5.11 video, which uses every layout). The format is documented at the top of `store-assets/scripts/release-video.ts`:
- `desktop`, `mobile`: the released versions; leave one out for a single-app release.
- `scenes`: one per highlight, in the post's order. Each has an `eyebrow` (a MingCute icon name such as `refresh-2-line` and one to three words naming the area) and a two-line `headline` whose second line is wrapped in `*…*` for the accent gradient, then a one-sentence `sub`, or `items` (up to five one-line checks, for fixes), plus `chips` for a set of names such as languages.
- `visual`: `mac` (one Mac shot, or several that crossfade while the chips light up in step), `mac-phone` (a Mac and a phone for changes across devices, with an optional spinning `badge`) or `phones` (one or two phones, with an optional `callout` naming the shot whose region to enlarge).
- `duration` in ms, when the default 4.6 s does not suit a scene.
The copy is English, like the post. The intro ("Folo update time." with the versions) and the outro ("Update today." with folo.is) are fixed.
### Render and review
```bash
pnpm exec tsx store-assets/scripts/release-video.ts "$SCRATCH/promo/video.json" --captures <captures> --stills "$SCRATCH/promo/stills"
```
It prints the timeline and every shot with its capture time, writes one frame per scene and `contact-sheet.png`, and reports text that wraps or leaves its area as `PROBLEM` lines (exit code 1). Fix the spec until none are left, then look at each still: the screen matches the highlight, the copy reads well, nothing is cut off or shows through.
```bash
pnpm exec tsx store-assets/scripts/release-video.ts "$SCRATCH/promo/video.json" --captures <captures> --video "$SCRATCH/promo/folo-<versions>.mp4"
ffprobe -v error -show_entries stream=codec_name,width,height,pix_fmt,r_frame_rate:format=duration,size -of compact "$SCRATCH/promo/folo-<versions>.mp4"
```
Expect `h264`, 1920 × 1080, `yuv420p`, `30/1` and the timeline's length; X takes the file as it is. The video has no sound. Rendering needs Playwright's Chromium (`pnpm exec playwright install chromium`), `ffmpeg`, and network access for the web fonts.
## 6. Deliver
In one message:
- the post, the reply and both quote lines, each in its own code block with its weighted length
- the video and the contact sheet
- how to publish: attach the video to the post, reply with the links, then quote-post from the second account with one of the quote lines
Name anything left out and why, such as a highlight without a capture or a claim dropped because it was not verified.
@@ -0,0 +1,62 @@
#!/usr/bin/env node
// Helpers for the release posts on X.
// node x-post.mjs bold <version> prints the version in math bold, e.g. 1.15.0 → 𝐯𝟏.𝟏𝟓.𝟎
// node x-post.mjs count <file>... prints each file's weighted length; exits 1 above 280
// The count follows twitter-text v3: code points in LIGHT weigh 1 and everything else 2
// (CJK, emoji, math bold), an emoji sequence counts as one emoji, and a link counts as 23
// whatever its length. Links are found by their domain (scheme optional, common TLDs).
import { readFileSync } from "node:fs"
const LIMIT = 280
const LINK_WEIGHT = 23
const LIGHT = [
[0, 4351],
[8192, 8205],
[8208, 8223],
[8242, 8247],
]
const link = /(?:https?:\/\/)?(?:[a-z0-9-]+\.)+(?:com|is|io|org|net|app|dev|me|co|cc)(?:\/\S*)?/giu
const emoji =
/\p{Extended_Pictographic}(?:\uFE0F|\p{Emoji_Modifier})?(?:\u200D\p{Extended_Pictographic}(?:\uFE0F|\p{Emoji_Modifier})?)*|\p{Regional_Indicator}{2}|[#*0-9]\uFE0F?\u20E3/gu
export const weightedLength = (text) => {
let total = 0
const rest = text
.normalize("NFC")
.replaceAll(link, () => {
total += LINK_WEIGHT
return ""
})
.replaceAll(emoji, () => {
total += 2
return ""
})
for (const char of rest) {
const code = char.codePointAt(0)
total += LIGHT.some(([from, to]) => code >= from && code <= to) ? 1 : 2
}
return total
}
export const bold = (version) =>
`\u{1D42F}${[...version.replace(/^v/, "")]
.map((char) => (/\d/.test(char) ? String.fromCodePoint(0x1d7ce + Number(char)) : char))
.join("")}`
const [command, ...rest] = process.argv.slice(2)
if (command === "bold" && rest.length === 1) {
console.log(bold(rest[0]))
} else if (command === "count" && rest.length > 0) {
let over = false
for (const file of rest) {
const length = weightedLength(readFileSync(file === "-" ? 0 : file, "utf8").trimEnd())
over ||= length > LIMIT
console.log(
`${String(length).padStart(3)}/${LIMIT} ${file}${length > LIMIT ? " TOO LONG" : ""}`,
)
}
process.exitCode = over ? 1 : 0
} else {
console.error("Usage: x-post.mjs bold <version> | count <file|->...")
process.exitCode = 1
}
+2 -1
View File
@@ -1,6 +1,6 @@
---
name: desktop-release
description: Full Folo desktop release from dev to the stores. Drafts the changelog, picks the build or ota mode, bumps the version and opens the release PR, verifies the packages GitHub Actions builds for the PR and every change in the release, merges the PR, watches the tag and store builds, gets the Windows installer signed, publishes the GitHub release, and submits the Mac App Store and Microsoft Store builds for review. Use when the user asks to release, ship or publish the desktop app.
description: Full Folo desktop release from dev to the stores. Drafts the changelog, picks the build or ota mode, bumps the version and opens the release PR, verifies the packages GitHub Actions builds for the PR and every change in the release, merges the PR, watches the tag and store builds, gets the Windows installer signed, publishes the GitHub release, submits the Mac App Store and Microsoft Store builds for review, and drafts the announcement post and promo video. Use when the user asks to release, ship or publish the desktop app.
disable-model-invocation: true
argument-hint: "[optional notes, e.g. a preferred mode or the OTA runtime]"
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
@@ -29,6 +29,7 @@ Read `.agents/skills/desktop-release/SKILL.md` completely before doing anything,
- Uploads: `file_upload` takes under 10 MB per call and the limit counts across a whole `browser_batch`, so call it on its own. The `.appx` goes up with the chunked recipe in `references/store-submissions.md`; find the injected input with `find` "Folo chunk input".
- Chrome quirks: when a ref click does nothing, take a screenshot and click by coordinates. Extension output containing `key=value` query strings can come back as "[BLOCKED: Cookie/query string data]"; read such values from the DOM in pieces.
- Report (C9): reply in the user's language and send the key screenshots and `verification.md` with SendUserFile when available. Save new learnings about the consoles or the pipeline to memory.
- Announcement kit (C10): follow `.claude/skills/release-promo/SKILL.md`.
## Limits that apply to Claude
+2 -1
View File
@@ -1,6 +1,6 @@
---
name: mobile-release
description: Full Folo mobile release from dev to the stores. Decides between a store release and an OTA-only release, drafts the changelog, bumps the version and opens the release PR to mobile-main, verifies the PR's CI-built Android package and the iOS build on a simulator along with every change in the release, merges the PR, watches the build or OTA workflows, publishes the GitHub release, and submits the iOS build for App Store review and the Android build for Google Play review. Use when the user asks to release, ship or publish the mobile app.
description: Full Folo mobile release from dev to the stores. Decides between a store release and an OTA-only release, drafts the changelog, bumps the version and opens the release PR to mobile-main, verifies the PR's CI-built Android package and the iOS build on a simulator along with every change in the release, merges the PR, watches the build or OTA workflows, publishes the GitHub release, submits the iOS build for App Store review and the Android build for Google Play review, and drafts the announcement post and promo video. Use when the user asks to release, ship or publish the mobile app.
disable-model-invocation: true
argument-hint: "[optional notes, e.g. 'ota' or 'minor bump']"
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
@@ -27,6 +27,7 @@ Read `.agents/skills/mobile-release/SKILL.md` completely before doing anything,
- Chrome quirks: when a ref click does nothing (the Google account chooser is one such place), take a screenshot and click by coordinates. Extension output containing `key=value` query strings can come back as "[BLOCKED: Cookie/query string data]"; read such values from the DOM in pieces.
- Report (C8): reply in the user's language and send the key screenshots and `verification.md` with SendUserFile when available. Save new learnings about the consoles or the pipeline to memory.
- Announcement kit (C9): follow `.claude/skills/release-promo/SKILL.md`.
## Limits that apply to Claude
+16
View File
@@ -0,0 +1,16 @@
---
name: release-promo
description: Announcement kit for a Folo release - the X post with the highlights, its reply with the release links, a one-sentence quote line for the second account, and a 1080p promo video rendered from real app captures. The desktop and mobile release skills end with it; also use it when the user asks for promo material for a release.
argument-hint: "[releases, e.g. 'desktop 1.15.0 mobile 0.5.11'; defaults to the releases this session shipped]"
allowed-tools: Bash, Read, Write, Edit, Glob, Grep
---
# Release Promo (Claude Code)
Read `.agents/skills/release-promo/SKILL.md` completely, then follow it. That file is the flow shared by every agent; this one only maps it onto Claude Code's tools. Change the flow there, not here.
- Working directory: `$SCRATCH/promo` when a release skill runs it, `<session scratchpad>/release-promo` otherwise.
- Review: look at `contact-sheet.png` and each still with Read before rendering the video.
- Video: run the `--video` render as a background Bash command (`run_in_background: true`) and finish the drafts meanwhile.
- Deliver: reply in the user's language with each draft in a fenced `text` block, and send the video and the contact sheet with SendUserFile.
- Never post: don't open X or any other social account in Claude in Chrome, computer-use or any other tool for this skill.