| name | ars:prepare-youtube |
| description | Generate 3 YouTube metadata candidates (title/description/tags) grounded in episode context, let the user pick one, then mark the artifact ready. |
| argument-hint | <epId> |
| model | claude-sonnet-4-6 |
| effort | medium |
Run npx ars prepare youtube <epId> from the repo root.
This CLI call creates output/publish/<activeSeries>/<epId>/prepare-youtube.md and prepare-youtube.json. The JSON contains chapters (timestamps derived from step durations / subtitles) and empty youtube.candidates[] + youtube.selected: null. The skill then fills candidates, presents them to the user, waits for a pick, applies the selected candidate to metadata.youtube in the episode source, and mirrors that applied metadata back into the prepare artifact.
Two-pass flow
This skill runs in two passes. Do not collapse them.
Pass 1 — generate candidates:
- Run
npx ars prepare youtube <epId> first. It is idempotent.
- Read
SERIES_GUIDE.md at repo root to inherit tone, audience, and packaging heuristics.
- Read
output/publish/<activeSeries>/<epId>/prepare-youtube.md as source context (contains episode info, steps summary, and chapters).
- Read
output/publish/<activeSeries>/<epId>/prepare-youtube.json to see current state.
- Generate exactly 3 candidates (
youtube-1, youtube-2, youtube-3). Each candidate must have a genuinely different angle — not three paraphrases of the same take. Good differentiation examples:
- candidate 1: pain-point entry targeting the widest viewer
- candidate 2: technical / specific-object hook targeting the engineer subset
- candidate 3: stance / opinion hook leveraging the host persona from SERIES_GUIDE.md
- For each candidate:
title — tight, under 70 chars, front-load the hook
description — full body grounded in the episode, ending with a blank line then the chapter block (see below)
tags — 8-15 relevant tags, include the channel tag (e.g. 人蔘TryCatch)
rationale — one sentence explaining why this packaging works for this episode
warnings — array of strings; empty if none (e.g. claim boundaries, clickbait risk)
- Chapter block: every description MUST end with the exact chapter list from
artifact.chapters. Do NOT recalculate timestamps, do NOT drop chapters, do NOT paraphrase labels. Format:
章節:
00:00 開場
00:18 Wintel 鎖定飛輪
...
- Update
prepare-youtube.json:
- set
youtube.candidates to the 3 candidates
- keep
status: "pending-review" and youtube.selected: null
- do NOT touch
youtube.title / youtube.description / youtube.tags in pass 1
- Update
prepare-youtube.md — append (or rewrite) a ## YouTube Candidates section with all 3 candidates shown as:
### youtube-1
- Title: ...
- Tags: tag1, tag2, ...
- Rationale: ...
- Warnings: ...
```text
<full description including chapter block>
- Present a concise summary to the user in chat (1 line per candidate: id + title + one-line rationale) and ask them to pick one by id, e.g. "挑哪個?youtube-1 / youtube-2 / youtube-3,或是要我重跑/微調。"
Pass 2 — apply selection:
Triggered when the user replies with a candidate id (or "選 1" / "用 2" / similar):
- Re-read
prepare-youtube.json to confirm candidates exist.
- Find the selected candidate in
youtube.candidates[].
- Update the JSON:
status: "ready"
youtube.selected: "<chosen id>"
youtube.title, youtube.description, youtube.tags = flattened fields from the selected candidate, matching the episode source metadata
- Update
src/episodes/<activeSeries>/<epId>.ts so the exported Episode object's metadata object contains youtube: { title, description, tags }, and those three fields exactly match the selected candidate. This must be nested under metadata (the same object that already contains title / subtitle), not written as a top-level youtube field and not left only in prepare-youtube.json. The episode source is the applied metadata source of truth shown by Studio after selection.
- Update the markdown: prepend a
## Selected block near the top showing which candidate is chosen. Keep the full candidates section below for audit.
- Run
npx ars episode validate <epId>.
- Report to the user: chosen id, final title, and remind them
/ars:publish-youtube <epId> is next. Do not auto-publish.
Rules
- Treat prepare as the mandatory first stage of the YouTube flow.
- The CLI command is idempotent and safe to re-run; re-running preserves existing candidates if they are still good, or the skill can regenerate them on explicit user request ("重跑", "換一批").
- If the user edits a candidate by hand (tweaks title/tags in JSON) and says "用這個", treat it as selection — still flatten to title/description/tags and mark ready.
- Studio Prepare may submit prepare intents instead of the user invoking this skill directly. Treat
prepare:youtube:generate / feedback.kind === 'prepare-generate' as Pass 1 and prepare:<candidateId>:select / feedback.kind === 'prepare-select' as Pass 2.
- Legacy
feedback.kind === 'prepare-trigger' is ambiguous. Always inspect target.anchorMeta.hash before deciding whether the intent is generate, select, or edit.
- When this skill is invoked because of a Studio intent, resolve that intent with
npx ars studio intent resolve ... after the matching pass. Do not use clear for generated candidates, selected candidates, or no-op duplicate prepare actions.
- Never generate chapter timestamps yourself. Only copy from
artifact.chapters.
- Leave the artifact in human-review state after pass 1; do not auto-publish after pass 2 either.