{"id":"bd4b9efb-8fd9-4dda-8c93-597f1932ca11","entityType":"agent","slug":"clawhub-justinhartbiz-yt-to-blog","name":"YT-to-Blog Content Engine","canonicalUrl":"https://www.xpersona.co/agent/clawhub-justinhartbiz-yt-to-blog","canonicalPath":"/agent/clawhub-justinhartbiz-yt-to-blog","generatedAt":"2026-10-10T08:43:20.077Z","source":"CLAWHUB","claimStatus":"UNCLAIMED","verificationTier":"NONE","summary":{"evidence":{"source":"editorial-content","verified":true,"confidence":"high","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":null},"description":"Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread → vertical video clips via HeyGen AI avatar. One URL in, entire content suite out. Use when asked to: \"turn this video into content\", \"create a content suite from this YouTube video\", \"write a blog from this video\", \"repurpose this video\", or any video-to-multi-platform content request. Can run the full pipeline or individual steps. Skill: YT-to-Blog Content Engine Owner: justinhartbiz Summary: Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread → vertical video clips via HeyGen AI avatar. One URL in, entire content suite out. Use when asked to: \"turn this video into content\", \"create a content suite from this YouTube video\", \"write a blog from this video\", \"repurpose this video\", or any video-to-multi","descriptionLabel":"Technical summary","evidenceSummary":"Capability contract not published. No trust telemetry is available yet. 1.7K downloads reported by the source. Last updated 4/15/2026.","installCommand":"clawhub skill install kn7axjxn3pz992e0fw2v62mngs80f166:yt-to-blog","sourceUrl":"https://clawhub.ai/justinhartbiz/yt-to-blog","homepage":"https://clawhub.ai/justinhartbiz/yt-to-blog","primaryLinks":[{"label":"View on ClawHub","url":"https://clawhub.ai/justinhartbiz/yt-to-blog","kind":"source"}],"safetyScore":84,"overallRank":62,"popularityScore":65,"trustScore":null,"claimedByName":null,"isOwner":false,"seoDescription":"Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread → vertical video clips via HeyGen AI avatar. One URL in, entire "},"coverage":{"evidence":{"source":"public-profile","verified":false,"confidence":"medium","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":null},"protocols":[{"protocol":"OPENCLEW","label":"OpenClaw","status":"self-declared","notes":"Declared in the public agent profile."}],"capabilities":[],"verifiedCount":0,"selfDeclaredCount":1,"capabilityMatrix":{"rows":[{"key":"OPENCLEW","type":"protocol","support":"unknown","confidenceSource":"profile","notes":"Listed on profile"}],"flattenedTokens":"protocol:OPENCLEW|unknown|profile"}},"adoption":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":null},"stars":null,"forks":null,"downloads":1684,"packageName":null,"latestVersion":"2.0.0","tractionLabel":"1.7K downloads"},"release":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"medium","updatedAt":"2026-02-28T20:39:02.122Z","emptyReason":null},"lastUpdatedAt":"2026-04-15T00:45:39.800Z","lastCrawledAt":"2026-02-28T20:39:02.122Z","lastIndexedAt":null,"nextCrawlAt":"2026-03-01T20:39:02.122Z","lastVerifiedAt":null,"highlights":[{"version":"2.0.0","createdAt":"2026-02-05T22:54:03.686Z","changelog":"Major update: Full content pipeline - YouTube → Blog → Substack → Twitter → HeyGen avatar videos. Added Facebook Group reminders, voice ID fix, video post-processing.","fileCount":8,"zipByteSize":16920},{"version":"1.0.0","createdAt":"2026-02-03T02:01:27.708Z","changelog":"Initial release: YouTube to blog post to Substack draft. Transcript fetch, configurable author voice, browser-based Substack posting (no API needed).","fileCount":6,"zipByteSize":9613}]},"execution":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No published capability contract is available yet."},"installCommand":"clawhub skill install kn7axjxn3pz992e0fw2v62mngs80f166:yt-to-blog","setupComplexity":"low","setupSteps":["Setup complexity is LOW. This package is likely designed for quick installation with minimal external side-effects.","Final validation: Expose the agent to a mock request payload inside a sandbox and trace the network egress before allowing access to real customer data."],"contract":{"contractStatus":"missing","authModes":[],"requires":[],"forbidden":[],"supportsMcp":false,"supportsA2a":false,"supportsStreaming":false,"inputSchemaRef":null,"outputSchemaRef":null,"dataRegion":null,"contractUpdatedAt":null,"sourceUpdatedAt":null,"freshnessSeconds":null},"invocationGuide":{"preferredApi":{"snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/trust"},"curlExamples":["curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/snapshot\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/contract\"","curl -s \"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/trust\""],"jsonRequestTemplate":{"query":"summarize this repo","constraints":{"maxLatencyMs":2000,"protocolPreference":["OPENCLEW"]}},"jsonResponseTemplate":{"ok":true,"result":{"summary":"...","confidence":0.9},"meta":{"source":"CLAWHUB","generatedAt":"2026-10-10T08:43:20.076Z"}},"retryPolicy":{"maxAttempts":3,"backoffMs":[500,1500,3500],"retryableConditions":["HTTP_429","HTTP_503","NETWORK_TIMEOUT"]}},"endpoints":{"dossierUrl":"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/dossier","snapshotUrl":"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/snapshot","contractUrl":"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/contract","trustUrl":"https://www.xpersona.co/api/v1/agents/clawhub-justinhartbiz-yt-to-blog/trust"}},"reliability":{"evidence":{"source":"runtime-metrics","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No trust, reliability, or runtime telemetry is available."},"trust":{"status":"unavailable","handshakeStatus":"UNKNOWN","verificationFreshnessHours":null,"reputationScore":null,"p95LatencyMs":null,"successRate30d":null,"fallbackRate":null,"attempts30d":null,"trustUpdatedAt":null,"trustConfidence":"unknown","sourceUpdatedAt":null,"freshnessSeconds":null},"decisionGuardrails":{"doNotUseIf":["Contract metadata is missing or unavailable for deterministic execution."],"safeUseWhen":[],"riskFlags":["missing_or_unavailable_contract","trust_data_unavailable","schema_references_missing"],"operationalConfidence":"low"},"executionMetrics":{"observedLatencyMsP50":null,"observedLatencyMsP95":null,"estimatedCostUsd":null,"uptime30d":null,"rateLimitRpm":null,"rateLimitBurst":null,"lastVerifiedAt":null,"verificationSource":null},"runtimeMetrics":{"successRate":null,"avgLatencyMs":null,"avgCostUsd":null,"hallucinationRate":null,"retryRate":null,"disputeRate":null,"p50Latency":null,"p95Latency":null,"lastUpdated":null}},"benchmarks":{"evidence":{"source":"no-benchmark-data","verified":false,"confidence":"low","updatedAt":null,"emptyReason":"No benchmark suites or observed failure patterns are available."},"suites":[],"failurePatterns":[]},"artifacts":{"evidence":{"source":"CLAWHUB","verified":false,"confidence":"high","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":null},"readme":"Skill: YT-to-Blog Content Engine\n\nOwner: justinhartbiz\n\nSummary: Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread → vertical video clips via HeyGen AI avatar. One URL in, entire content suite out. Use when asked to: \"turn this video into content\", \"create a content suite from this YouTube video\", \"write a blog from this video\", \"repurpose this video\", or any video-to-multi-platform content request. Can run the full pipeline or individual steps.\n\nTags: latest:2.0.0\n\nVersion history:\n\nv2.0.0 | 2026-02-05T22:54:03.686Z | user\n\nMajor update: Full content pipeline - YouTube → Blog → Substack → Twitter → HeyGen avatar videos. Added Facebook Group reminders, voice ID fix, video post-processing.\n\nv1.0.0 | 2026-02-03T02:01:27.708Z | user\n\nInitial release: YouTube to blog post to Substack draft. Transcript fetch, configurable author voice, browser-based Substack posting (no API needed).\n\nArchive index:\n\nArchive v2.0.0: 8 files, 16920 bytes\n\nFiles: config.json (782b), references/config-template.md (236b), references/voice-guide-template.md (2931b), references/voice-guide.md (2816b), scripts/fetch_transcripts.py (4897b), setup.sh (6809b), SKILL.md (19612b), _meta.json (129b)\n\nFile v2.0.0:SKILL.md\n\n---\nname: yt-to-blog\ndescription: >\n  Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread →\n  vertical video clips via HeyGen AI avatar. One URL in, entire content suite out. Use when asked to:\n  \"turn this video into content\", \"create a content suite from this YouTube video\", \"write a blog from\n  this video\", \"repurpose this video\", or any video-to-multi-platform content request.\n  Can run the full pipeline or individual steps.\n---\n\n# YT-to-Blog Content Engine\n\nYouTube URL → blog post + Substack + tweets + vertical video clips. The whole content machine.\n\n## Pipeline Overview\n\n```\nYouTube URL\n  ↓\n① Transcript (summarize CLI)\n  ↓\n② Blog Draft (AI-written in your voice)\n  ↓\n③ Substack Publish (browser automation)\n  ↓\n④ X/Twitter Post (bird CLI)\n  ↓\n④b Facebook Group (optional reminder)\n  ↓\n⑤ Script Splitter (extract hook moments)\n  ↓\n⑥ HeyGen Videos (AI avatar vertical clips)\n  ↓\n⑦ Post-Processing (ffmpeg crop/scale)\n  ↓\n📁 Output Folder (blog.md, videos, tweet.txt, URLs)\n```\n\n**One URL in → Five platforms out.** Run the whole thing or any step individually.\n\n---\n\n## First-Time Setup Wizard\n\nWalk the user through this on first use. It takes ~10 minutes once, then never again.\n\n### Step 1: Check Dependencies\n\nRun the setup script to check what's installed:\n\n```bash\nbash skills/yt-content-engine/setup.sh\n```\n\nRequired CLIs:\n| Tool | Purpose | Install |\n|------|---------|---------|\n| `summarize` | YouTube transcript extraction | `brew install steipete/tap/summarize` |\n| `bird` | X/Twitter posting | `brew install steipete/tap/bird` |\n| `ffmpeg` | Video post-processing | `brew install ffmpeg` |\n| `curl` | API calls to HeyGen | Usually pre-installed on macOS |\n| `python3` | Helper scripts | Usually pre-installed on macOS |\n\nIf anything is missing, tell the user what to install and wait for confirmation.\n\n### Step 2: HeyGen API Key\n\n1. Tell the user: \"Go to https://app.heygen.com/settings — grab your API key from the API section.\"\n2. If they don't have a HeyGen account: \"Sign up at https://heygen.com — the free tier gives you a few credits to test with.\"\n3. Save the key to `config.json` (see config schema below).\n4. Test it:\n\n```bash\ncurl -s -H \"X-Api-Key: API_KEY_HERE\" https://api.heygen.com/v2/avatars | python3 -c \"import sys,json; d=json.load(sys.stdin); print('✅ API key works!' if 'data' in d else '❌ Invalid key')\"\n```\n\n### Step 3: HeyGen Avatar Setup\n\nTell the user:\n\n> \"For vertical video clips, you need a HeyGen avatar. Here's what matters:\n>\n> **Record in PORTRAIT mode** (hold your phone vertically). This is critical — if you record landscape, the avatar will be a small strip in the center of a 9:16 frame and we'll need to crop/scale it (which works but loses quality).\n>\n> Go to https://app.heygen.com/avatars → Create Instant Avatar → follow their recording guide. Stand in good lighting, look at camera, speak naturally for 2+ minutes.\n>\n> Once created, grab your Avatar ID from the avatar details page.\"\n\nList their existing avatars to help them pick. Note: the avatars endpoint returns both custom and stock avatars — filter for the user's custom ones (they typically appear first and have personal names):\n\n```bash\ncurl -s -H \"X-Api-Key: API_KEY\" https://api.heygen.com/v2/avatars | python3 -c \"\nimport sys, json\ndata = json.load(sys.stdin)\nfor a in data.get('data', {}).get('avatars', []):\n    print(f\\\"  {a['avatar_id']} — {a.get('avatar_name', 'unnamed')}\\\")\n\"\n```\n\n### Step 4: HeyGen Voice Clone\n\nTell the user:\n\n> \"Go to https://app.heygen.com/voice-clone → Clone your voice. Upload a clean audio sample (1-2 min of you speaking naturally). HeyGen will create a voice ID.\n>\n> Once done, grab your Voice ID from the voice settings.\"\n\nList their voices. User's cloned voices typically appear first; stock voices come after:\n\n```bash\ncurl -s -H \"X-Api-Key: API_KEY\" https://api.heygen.com/v2/voices | python3 -c \"\nimport sys, json\ndata = json.load(sys.stdin)\nfor v in data.get('data', {}).get('voices', []):\n    print(f\\\"  {v['voice_id']} — {v.get('name', 'unnamed')} ({v.get('language', '?')})\\\")\n\"\n```\n\n⚠️ **IMPORTANT:** Use the FULL voice_id (e.g., `69da9c9bca78499b98fdac698d2a20cd`), not a truncated version. The API will return \"Voice validation failed\" if you use a shortened ID.\n\n### Step 5: Substack Login\n\nSubstack has no API — posting requires browser automation.\n\n1. Open the OpenClaw managed browser: use browser tool with `profile=\"openclaw\"`\n2. Navigate to `https://substack.com/sign-in`\n3. Help the user log in with their credentials\n4. Verify access by navigating to their publication dashboard\n5. Save the publication URL to `config.json`\n\nThe browser session persists across restarts. One-time setup.\n\n### Step 6: Save Config\n\nCreate `skills/yt-content-engine/config.json` (relative to your workspace):\n\n```json\n{\n  \"heygen\": {\n    \"apiKey\": \"YOUR_API_KEY\",\n    \"avatarId\": \"YOUR_AVATAR_ID\",\n    \"voiceId\": \"YOUR_VOICE_ID\"\n  },\n  \"substack\": {\n    \"publication\": \"yourblog.substack.com\"\n  },\n  \"twitter\": {\n    \"handle\": \"@yourhandle\"\n  },\n  \"author\": {\n    \"voice\": \"Description of your writing voice and style\",\n    \"name\": \"Your Name\"\n  },\n  \"video\": {\n    \"clipCount\": 5,\n    \"maxClipSeconds\": 60,\n    \"cropMode\": \"auto\"\n  }\n}\n```\n\n**Tip:** If the user already has a voice guide from the `yt-to-blog` skill, read it from `skills/yt-to-blog/references/voice-guide.md` and use it for the `author.voice` field.\n\n### Step 7: Verify Everything\n\nRun the setup script with the config in place:\n\n```bash\nbash skills/yt-content-engine/setup.sh\n```\n\nIt will test each component and report status.\n\n---\n\n## How to Invoke\n\n### Full Pipeline\n```\n\"Turn this into a full content suite: https://youtu.be/XXXXX\"\n\"Content engine this video: [URL]\"\n\"Run the full pipeline on [URL]\"\n```\n\n### Individual Steps\n```\n\"Just get me the transcript from [URL]\"\n\"Write a blog post from [URL]\" (steps 1-2)\n\"Post this to Substack\" (step 3, after blog exists)\n\"Tweet about this blog post\" (step 4)\n\"Generate video clips from this blog\" (steps 5-7)\n\"Just split this into scripts\" (step 5 only)\n```\n\n---\n\n## Pipeline Steps\n\n### Step ①: Transcript\n\nCreate the output directory for this run, then fetch the YouTube transcript:\n\n```bash\nmkdir -p /tmp/yt-content-engine/output-$(date +%Y-%m-%d)/scripts\nmkdir -p /tmp/yt-content-engine/output-$(date +%Y-%m-%d)/videos\n```\n\n```bash\nsummarize \"YOUTUBE_URL\" --extract > /tmp/yt-content-engine/transcript.txt\n```\n\nThe `--extract` flag prints the raw transcript without LLM summarization. Read the output. If it fails (no captions available), try with `--youtube yt-dlp` for auto-generated captions, or tell the user and suggest they provide a manual transcript.\n\n### Step ②: Blog Draft\n\nTransform the transcript into a polished long-form blog post.\n\n**Load the author voice** from `config.json` → `author.voice`. If a more detailed voice guide exists at `skills/yt-to-blog/references/voice-guide.md`, read and use that too.\n\n**Analysis phase** — before writing, extract from the transcript:\n- Core thesis — the single strongest argument or revelation\n- Key data points — statistics, quotes, dates, names\n- Narrative moments — anecdotes, examples, scenes\n- Source links — URLs, studies, references mentioned\n- Missing context — what does the reader need that the video assumed?\n\n**Writing structure:**\n1. **Cold open (1-3 paragraphs):** Scene-setting. Specific, sensory, emotional hook before data.\n2. **Thesis pivot (1 paragraph):** Connect scene to the bigger story.\n3. **Data body (5-15 paragraphs):** Alternate data and editorial. Each stat gets a punch line. Subheadings for major breaks only.\n4. **Callback (1-2 paragraphs):** Return to opening scene/metaphor.\n5. **Closing (3-6 short paragraphs):** Escalating fragments. Final hammer line.\n\n**Writing rules:**\n- Vary sentence length dramatically — long data sentences, then short punches\n- Em dashes for asides, not parentheses\n- Sentence fragments for emphasis\n- No bullet lists in the body — narrative flow\n- Inline source links, no footnotes\n- No \"in conclusion\" or \"to summarize\"\n- Credit video source naturally: \"As [Name] put it...\" with link\n- Target: 1,500-3,000 words\n\n**Generate 3-5 headline options** with distinct strategies (contrast/irony, revelation, moral framing, callback). Each with a subtitle. Let the user pick.\n\nSave the final draft to the output folder as `blog.md`.\n\n### Step ③: Substack Publish\n\nPost the blog to Substack via browser automation.\n\n1. Read `config.json` → `substack.publication`\n2. Open managed browser (`profile=\"openclaw\"`)\n3. Navigate to `https://PUBLICATION.substack.com/publish/post`\n4. Click the title field, type the title\n5. Click the subtitle area, type the subtitle\n6. Click the body area\n7. Write markdown to a temp file, copy to clipboard (`pbcopy < /tmp/post.md`), paste into editor (Meta+v)\n8. Substack auto-saves as draft\n\n**Known issues:**\n- Em dashes (`—`) may garble as `,Äî` during clipboard paste → find/replace after paste\n- Large posts: pause briefly between paste and verification\n- Verify draft at `https://PUBLICATION.substack.com/publish`\n\n**Default: save as draft.** Only publish if the user explicitly says \"publish it\" — always confirm first.\n\nSave the Substack URL to `output/substack-url.txt`.\n\n### Step ④: X/Twitter Post\n\nCompose and post using the `bird` CLI.\n\n**Compose the tweet/thread:**\n- If the blog has a single killer hook → single tweet with link\n- If there are multiple strong points → thread (3-5 tweets)\n- Include the Substack URL\n- Match the author's voice but punchier — tweets are hooks, not summaries\n- Use the handle from `config.json` → `twitter.handle`\n\n**Post with bird:**\n```bash\n# Single tweet\nbird tweet \"Your tweet text here\"\n\n# Thread (post first tweet, then reply to it)\nbird tweet \"Tweet 1 text here\"\n# Note the returned tweet ID, then:\nbird reply TWEET_ID \"Tweet 2 text here\"\n# And chain:\nbird reply TWEET_2_ID \"Tweet 3 text here\"\n```\n\n**Always show the user the tweet text before posting and get confirmation.**\n\nSave tweet text to `output/tweet.txt`.\n\n### Step ④b: Facebook Group (Optional)\n\nIf `config.json` includes a `facebook.group` URL, remind the user to post to their Facebook Group.\n\n**Note:** Facebook Group API posting is heavily restricted. Browser automation is unreliable due to Facebook's anti-bot measures. Best approach:\n\n1. Draft a Facebook post version of the content (shorter, more casual than tweet)\n2. Save to `output/facebook-post.txt`\n3. Remind the user: \"Don't forget to post to [Group Name] — here's your draft\"\n4. User posts manually\n\nThis keeps Facebook distribution in the workflow without fighting their API restrictions.\n\n### Step ⑤: Script Splitter\n\nExtract 3-5 \"hook moments\" from the blog post and rewrite each as a spoken-word script for vertical video.\n\n**What to look for** (scan the blog for these patterns):\n1. **Hook/Controversy** — the most provocative claim, the thing that makes people stop scrolling\n2. **Data Bomb** — a surprising statistic or fact that reframes understanding\n3. **Counterintuitive Take** — something that contradicts conventional wisdom\n4. **Emotional Moment** — a story, anecdote, or human element that creates connection\n5. **Call-to-Action Closer** — a rallying cry, challenge, or \"what you should do now\"\n\nNot every blog will have all five. Extract what's there. Minimum 3 clips.\n\n**Rewrite rules for spoken delivery:**\n- **Hook first** — open with the most attention-grabbing line. No preamble.\n- **Conversational** — write for speaking, not reading. Contractions, natural rhythm.\n- **30-60 seconds each** — roughly 75-150 words per clip\n- **Self-contained** — each clip must work on its own, no \"as I mentioned earlier\"\n- **End with punch** — close on the strongest line, not a trailing thought\n- **No stage directions** — just the words to speak, nothing else\n\n**Format each script:**\n```\nCLIP 1: [descriptive title]\n---\n[Script text here, 75-150 words]\n```\n\nUse `config.json` → `video.clipCount` for the target number of clips (default: 5).\nUse `config.json` → `video.maxClipSeconds` for max duration (default: 60).\n\nSave scripts to `output/scripts/clip-1.txt`, `clip-2.txt`, etc.\n\n### Step ⑥: HeyGen Video Generation\n\nSubmit each script to HeyGen API v2 to generate AI avatar videos.\n\n**Read config:**\n```bash\n# Parse config.json\nAPI_KEY=$(python3 -c \"import json; c=json.load(open('config.json')); print(c['heygen']['apiKey'])\")\nAVATAR_ID=$(python3 -c \"import json; c=json.load(open('config.json')); print(c['heygen']['avatarId'])\")\nVOICE_ID=$(python3 -c \"import json; c=json.load(open('config.json')); print(c['heygen']['voiceId'])\")\n```\n\n**For each script, submit a video generation request:**\n\n```bash\ncurl -s -X POST \"https://api.heygen.com/v2/video/generate\" \\\n  -H \"X-Api-Key: $API_KEY\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"video_inputs\": [{\n      \"character\": {\n        \"type\": \"avatar\",\n        \"avatar_id\": \"'\"$AVATAR_ID\"'\",\n        \"avatar_style\": \"normal\"\n      },\n      \"voice\": {\n        \"type\": \"text\",\n        \"input_text\": \"'\"$(cat output/scripts/clip-1.txt)\"'\",\n        \"voice_id\": \"'\"$VOICE_ID\"'\"\n      }\n    }],\n    \"dimension\": {\n      \"width\": 1080,\n      \"height\": 1920\n    }\n  }'\n```\n\n**Parse the response** to get `video_id`:\n```python\nimport json\nresponse = json.loads(response_text)\nvideo_id = response[\"data\"][\"video_id\"]\n```\n\n**Submit ALL clips before polling.** HeyGen renders in parallel — submit all scripts first, collect all video_ids, then poll them all. This cuts total render time from N×3min to ~3min.\n\n**Poll for completion** (every 15 seconds, timeout after 10 minutes):\n\n```bash\ncurl -s -H \"X-Api-Key: $API_KEY\" \\\n  \"https://api.heygen.com/v1/video_status.get?video_id=$VIDEO_ID\" \\\n  | python3 -c \"import sys,json; d=json.load(sys.stdin)['data']; print(d['status'], d.get('video_url',''))\"\n```\n\nStatuses: `pending` → `processing` → `completed` (with `video_url`) or `failed` (with `error`).\n\n**Download completed videos:**\n```bash\ncurl -L -o \"output/videos/clip-1-raw.mp4\" \"$VIDEO_URL\"\n```\n\n**Credit note:** ~1 credit per 1 minute of video. A typical 5-clip run uses ~3 credits. Warn the user about credit usage before submitting.\n\n### Step ⑦: Video Post-Processing\n\nIf the avatar was recorded in landscape (common), the 9:16 video will show a small avatar strip centered in a large frame with background fill. Fix this with ffmpeg.\n\n**Check `config.json` → `video.cropMode`:**\n- `\"auto\"` — detect and crop automatically\n- `\"portrait\"` — skip cropping (avatar was recorded in portrait)\n- `\"manual\"` — ask user for crop coordinates\n\n**Auto-crop pipeline:**\n\n```bash\n# 1. Detect content bounds by scanning center column for non-background pixels\n# Extract a single frame\nffmpeg -i input.mp4 -vframes 1 -y /tmp/frame.png\n\n# 2. Use ffmpeg cropdetect to find content bounds\nffmpeg -i input.mp4 -vf \"cropdetect=24:16:0\" -frames:v 30 -f null - 2>&1 | grep cropdetect\n\n# Parse the crop values from output: crop=W:H:X:Y\n\n# 3. Crop content strip, scale up, center-crop to 1080x1920\nffmpeg -i input.mp4 \\\n  -vf \"crop=DETECTED_W:DETECTED_H:DETECTED_X:DETECTED_Y,scale=1080:-1,crop=1080:1920:0:(ih-1920)/2\" \\\n  -c:a copy \\\n  -y output.mp4\n```\n\n**Alternative manual detection** (preferred — cropdetect often fails when background is white/light):\n\nHeyGen typically renders landscape avatars centered on a white/light background in the 9:16 frame.\nScan the center column for non-white pixels to find the actual content strip:\n\n```bash\n# Extract a frame, then scan center column for content bounds\nffmpeg -y -ss 5 -i input.mp4 -frames:v 1 /tmp/frame.png 2>/dev/null\n\nffmpeg -y -i /tmp/frame.png -vf \"crop=1:ih:iw/2:0,format=gray\" -f rawvideo -pix_fmt gray - 2>/dev/null | \\\n  python3 -c \"\nimport sys\ndata = sys.stdin.buffer.read()\nfirst = last = None\nfor i, b in enumerate(data):\n    if b < 240:  # Non-white pixel = actual content\n        if first is None: first = i\n        last = i\nif first is not None:\n    print(f'CONTENT_Y={first}')\n    print(f'CONTENT_HEIGHT={last - first}')\n    print(f'CENTER={( first + last) // 2}')\nelse:\n    print('No content bounds detected — avatar may already fill the frame')\n\"\n```\n\nThen crop the content strip, scale proportionally to fill width, and center-crop to 9:16:\n```bash\nffmpeg -y -i input.mp4 \\\n  -vf \"crop=iw:CONTENT_HEIGHT:0:CONTENT_Y,scale=-1:1920,crop=1080:1920:(ow-1080)/2:0\" \\\n  -c:v libx264 -crf 23 -preset fast -c:a aac \\\n  output.mp4\n```\n\n**Proven crop values for common HeyGen landscape avatars** (1080x1920 canvas):\n- Content strip typically at y≈656, height≈607px\n- Example: `crop=1080:607:0:656,scale=3413:1920,crop=1080:1920:1166:0`\n- Always detect per-video — avatar placement can shift\n\n**Save processed videos** to `output/videos/clip-1.mp4`, `clip-2.mp4`, etc.\n\nIf crop mode is `portrait`, just copy the raw files:\n```bash\ncp output/videos/clip-1-raw.mp4 output/videos/clip-1.mp4\n```\n\n### Step ⑧: Output\n\nOrganize everything in a dated output folder:\n\n```\noutput-YYYY-MM-DD/\n├── blog.md              # Final blog post\n├── tweet.txt            # Tweet text (posted or ready to post)\n├── substack-url.txt     # URL of Substack draft/post\n├── scripts/\n│   ├── clip-1.txt       # Spoken word scripts\n│   ├── clip-2.txt\n│   └── ...\n├── videos/\n│   ├── clip-1.mp4       # Final processed vertical videos\n│   ├── clip-2.mp4\n│   └── ...\n└── manifest.json        # Run metadata\n```\n\n**manifest.json:**\n```json\n{\n  \"source\": \"https://youtu.be/XXXXX\",\n  \"date\": \"2026-02-03\",\n  \"blog\": \"blog.md\",\n  \"substackUrl\": \"https://...\",\n  \"tweetUrl\": \"https://...\",\n  \"clips\": [\"clip-1.mp4\", \"clip-2.mp4\", \"...\"],\n  \"heygenCreditsUsed\": 3\n}\n```\n\nReport the summary to the user:\n- ✅ Blog post: X words\n- ✅ Substack: [URL] (draft/published)\n- ✅ Tweet: posted / ready to post\n- ✅ X video clips generated and processed\n- 💰 HeyGen credits used: ~X\n\n---\n\n## Config Reference\n\nConfig file: `skills/yt-content-engine/config.json` (relative to workspace root)\n\n| Key | Description | Default |\n|-----|-------------|---------|\n| `heygen.apiKey` | HeyGen API key | Required |\n| `heygen.avatarId` | Your HeyGen avatar ID | Required |\n| `heygen.voiceId` | Your cloned voice ID | Required |\n| `substack.publication` | Substack subdomain | Required |\n| `twitter.handle` | X/Twitter handle | Required |\n| `author.voice` | Writing style description | Recommended |\n| `author.name` | Author name for attribution | Recommended |\n| `video.clipCount` | Number of clips to generate | `5` |\n| `video.maxClipSeconds` | Max seconds per clip | `60` |\n| `video.cropMode` | `auto`, `portrait`, or `manual` | `auto` |\n\n---\n\n## Tips & Troubleshooting\n\n- **HeyGen rendering takes 2-3 min per clip.** Set expectations — a 5-clip run takes 10-15 minutes of render time.\n- **Portrait avatars save time.** No cropping needed. Worth re-recording if you use this regularly.\n- **Substack session expires?** Re-run the browser login step (Step 5 of setup).\n- **bird CLI not posting?** Run `bird auth` to re-authenticate.\n- **Bad crop detection?** Switch `cropMode` to `manual` and eyeball the content bounds from a frame export.\n- **HeyGen quota errors?** Check credits at https://app.heygen.com/settings — upgrade plan or reduce clip count.\n- **Transcript unavailable?** Some videos don't have captions. Try `summarize \"URL\" --extract --youtube yt-dlp` for auto-generated captions, or ask the user for a manual transcript.\n\nFile v2.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7axjxn3pz992e0fw2v62mngs80f166\",\n  \"slug\": \"yt-to-blog\",\n  \"version\": \"2.0.0\",\n  \"publishedAt\": 1770332043686\n}\n\nFile v2.0.0:references/config-template.md\n\n# Substack Configuration\n\nCopy this file to `config.md` and fill in your Substack details.\n\n- **URL:** https://xxxxx.substack.com\n- **Editor URL:** https://xxxxx.substack.com/publish/post\n- **Publication name:** [Your Publication Name]\n\nFile v2.0.0:references/voice-guide-template.md\n\n# Voice Guide — [Your Name]\n\nCopy this file to `voice-guide.md` and fill in your details. The more specific you are, the better the output matches your voice.\n\n**Fastest setup:** Paste 2-3 of your best existing posts and ask your agent:\n> \"Read these and build a voice guide for my writing style. Save to references/voice-guide.md\"\n\n---\n\n## Publication\n- **Substack:** xxxxx.substack.com\n- **Publication name:** [Your Publication Name]\n- **Audience:** [Who reads you? Demographics, interests, expertise level, political lean if relevant]\n- **Subscriber scale:** [Rough size — helps calibrate tone and assumptions]\n\n## Tone\n\nPick 2-3 that fit and describe what they mean for YOUR writing:\n\n- **Conversational authority** — smart friend explaining over drinks, not a lecturer\n- **Credible urgency** — passionate but grounded in evidence\n- **Dry wit** — humor through understatement, not punchlines\n- **Righteous anger** — moral clarity without screaming\n- **Academic rigor** — precise, hedged, citation-heavy\n- **Warm storyteller** — empathy-first, narrative-driven\n- **Provocateur** — deliberately challenges assumptions\n\nYour tone: [describe here]\n\n## Structure Pattern\n\nHow do your posts typically flow? Describe your go-to structure:\n\nExample:\n1. Cold open with a scene or anecdote\n2. Pivot to the bigger thesis\n3. Data/evidence with editorial commentary\n4. Callback to the opening\n5. Punchy closing with short paragraphs\n\nYour structure:\n1.\n2.\n3.\n4.\n5.\n\n## Sentence Style\n\n- **Length variation:** [dramatic swings between long and short? consistently medium? long and flowing?]\n- **Punctuation:** [em dashes? ellipses? semicolons? colons for lists?]\n- **Fragments:** [use sentence fragments for emphasis? e.g. \"Always has. Always will.\"]\n- **Direct address:** [talk to the reader? \"you\" or implied?]\n- **References:** [pop culture? academic? sports? everyday life? scripture?]\n- **Metaphor style:** [escalating? one central metaphor? sprinkled throughout?]\n\n## What You Do NOT Do\n\nOften more useful than what you do. Things that would make a post feel \"off\":\n\n- [ ] Corporate jargon or buzzwords\n- [ ] Hedge language (\"it could potentially suggest...\")\n- [ ] Bullet-point lists in the post body\n- [ ] \"In conclusion\" or \"to summarize\" transitions\n- [ ] Both-sides-ism when evidence is clear\n- [ ] Long academic footnotes (prefer inline links)\n- [ ] Sycophantic hedging\n- [ ] [Add your own...]\n\n## Topics & Themes\n\nWhat do you write about? List your lanes:\n\n-\n-\n-\n\n## Constraints\n\nHard rules the agent must follow:\n\n- [ ] Factually defensible — link sources, don't fabricate\n- [ ] No defamation or unsubstantiated claims about named individuals\n- [ ] [Audience-specific rules, e.g. \"maintain credibility with X audience\"]\n- [ ] [Content restrictions, e.g. \"avoid medical advice\"]\n- [ ] [Add your own...]\n\n## Sample Headlines\n\nList 5-10 of YOUR actual headlines so the agent matches the style:\n\n1.\n2.\n3.\n4.\n5.\n\nFile v2.0.0:references/voice-guide.md\n\n# CovidReason / Rational Ground — Justin Hart's Writing Voice\n\n## Publication\n- **Substack:** covidreason.substack.com (\"Rational Ground by Justin Hart\")\n- **Audience:** Conservative, COVID-skeptic, data-literate, politically engaged. Mix of professionals, parents, and policy skeptics.\n- **Subscriber base:** Large enough to drive 13K+ views on X promo posts\n\n## Voice Characteristics\n\n### Tone\n- **Credible urgency** — never tabloid hysteria\n- Righteous anger tempered by dry wit\n- Data-dense but narratively compelling\n- Conversational authority — writes like a smart friend explaining something over drinks, not a lecturer\n- Moral clarity without preachiness\n\n### Structure Pattern (The \"Lights Are Still On\" Template)\n1. **Cold open with a scene** — cinematic, specific, emotionally grounding (time, place, sensory details)\n2. **Pivot to thesis** — the scene isn't just a scene, it's a symptom of something bigger\n3. **Data barrage** — dense, linked, cited statistics that build the case. Each stat gets a one-line editorial punch\n4. **Rhetorical callbacks** — return to the opening scene or metaphor periodically to maintain narrative coherence\n5. **Escalating metaphors** — cockroaches in walls, planes losing altitude, overdue invoices\n6. **Punchy closing** — short paragraphs, sentence fragments, building to a final hammer line\n\n### Headline Style\n- Provocative but credible\n- Often uses contrast or irony: \"The Lights Are Still On, But Nobody's Shopping\"\n- Subtitle is the thesis statement in one sentence\n- Occasionally uses BREAKING: or imperative framing\n\n### Sentence Style\n- Varies sentence length dramatically (long data sentences, then one-word punches)\n- Em dashes for parenthetical asides\n- Sentence fragments for emphasis (\"Always has. Always will.\")\n- Direct address to reader implied but rarely explicit\n- Pop culture and everyday references to anchor abstract data\n\n### What He Does NOT Do\n- Corporate jargon or hedge language (\"it could potentially suggest...\")\n- Sycophantic hedging\n- Both-sides-ism when the data is clear\n- Long academic citations (uses inline links instead)\n- Bullet-point lists in the body (narrative flow > formatting)\n\n## Topics & Themes\n- COVID policy failures, lockdown harms, vaccine safety data\n- Government overreach and institutional credibility collapse\n- Economic data the mainstream isn't covering\n- Cultural commentary on media, tech, politics\n- Screwtape Letters-style satirical essays (ongoing series)\n- Occasional personal narrative\n\n## Key Constraints\n- Must be factually defensible — Justin's credibility depends on accuracy\n- Sources should be linked, not just cited\n- Charts/images embedded where data supports it\n- Avoid defamation or unsubstantiated claims about specific individuals\n- Strong opinion is fine; fabrication is not\n\nFile v2.0.0:config.json\n\n{\n  \"heygen\": {\n    \"apiKey\": \"sk_V2_hgu_k9fJHPzMY9_HcXzQD4ti6mSi6z7VOFPrhtIEVFnfGTS\",\n    \"avatarId\": \"eec9bc74c1204235a4e88019eb0c51b4\",\n    \"voiceId\": \"69da9c9bca78499b98fdac698d2a20cd\"\n  },\n  \"substack\": {\n    \"publication\": \"rationalground.com\"\n  },\n  \"twitter\": {\n    \"handle\": \"@justin_hart\"\n  },\n  \"facebook\": {\n    \"group\": \"https://www.facebook.com/groups/back2worktoday\"\n  },\n  \"author\": {\n    \"name\": \"Justin Hart\",\n    \"voice\": \"Direct, punchy, data-driven with narrative flair. Varies sentence length dramatically. Em dashes for asides. Sentence fragments for emphasis. No bullet lists in body - narrative flow only. Strong metaphors over abstract language. No hedge words.\"\n  },\n  \"video\": {\n    \"clipCount\": 5,\n    \"maxClipSeconds\": 60,\n    \"cropMode\": \"auto\"\n  }\n}\n\nArchive v1.0.0: 6 files, 9613 bytes\n\nFiles: references/config-template.md (236b), references/voice-guide-template.md (2931b), references/voice-guide.md (2816b), scripts/fetch_transcripts.py (4897b), SKILL.md (8235b), _meta.json (129b)\n\nFile v1.0.0:SKILL.md\n\n---\nname: yt-to-blog\ndescription: >\n  Transform YouTube videos or channels into long-form blog posts and publish to Substack via browser\n  automation (Substack has no API). Fetches transcripts, analyzes source material, crafts polished\n  essays in a configurable author voice, and posts drafts directly to Substack. Use when asked to:\n  write a blog post from a YouTube video, turn a video into a Substack article, draft a blog from\n  a transcript, \"write this up\", \"make a post from this video\", bulk-fetch channel transcripts,\n  or any video-to-written-content request. Also works for podcast-to-blog and lecture-to-blog.\n---\n\n# YT-to-Blog\n\nYouTube → transcript → blog post → Substack draft. End-to-end, no API needed.\n\n## First-Time Setup\n\nBefore using this skill, complete these one-time steps:\n\n### 1. Install Dependencies\n\n```bash\nbrew install steipete/tap/summarize   # Transcript extraction (primary)\nbrew install yt-dlp                    # Channel listing + fallback\npip3 install youtube-transcript-api    # Bulk transcript fetch\n```\n\n### 2. Configure Your Substack\n\nCreate `references/config.md` in this skill's directory with your Substack details:\n\n```markdown\n# Substack Configuration\n\n- **URL:** https://YOURNAME.substack.com\n- **Editor URL:** https://YOURNAME.substack.com/publish/post\n- **Publication name:** Your Publication Name\n```\n\nReplace `YOURNAME` with your actual Substack subdomain.\n\n### 3. Log Into Substack in OpenClaw Browser\n\nSubstack has no API — posting requires browser automation, which means OpenClaw's managed browser must be logged into your Substack account.\n\n```\n1. Open OpenClaw managed browser: use browser tool with profile=\"openclaw\"\n2. Navigate to https://substack.com/sign-in\n3. Log in with your Substack credentials\n4. Verify you can access your dashboard at https://YOURNAME.substack.com/publish\n```\n\nThe browser session persists across OpenClaw restarts. You only need to do this once (unless cookies expire).\n\n### 4. Create Your Voice Guide\n\nThe skill writes in YOUR voice. Create `references/voice-guide.md` with your writing style. See `references/voice-guide-template.md` for the template.\n\n**Fastest method:** Paste 2-3 of your best existing blog posts and ask your agent:\n> \"Read these posts and create a voice guide for my writing style. Save it to the yt-to-blog skill's references/voice-guide.md\"\n\n## Quick Start\n\nOnce setup is complete:\n\n```\nUser: \"Write a blog post from this video: https://youtu.be/XXXXX\"\nAgent: Fetches transcript → analyzes → drafts post → opens Substack → saves draft\n\nUser: \"Pull the last 20 videos from this channel and write a post from the best material\"\nAgent: Bulk-fetches → scans topics → asks which thread → drafts → posts\n\nUser: \"Just get me the transcript from this video\"\nAgent: Fetches and returns raw transcript text\n```\n\n## Workflow\n\n### Step 1: Fetch Transcripts\n\n**Single video** (preferred):\n```bash\nsummarize --extract \"YOUTUBE_URL\" > /tmp/transcript.txt\n```\nHandles captions, auto-generated subs, edge cases. Output includes duration and word count.\n\n**Bulk/channel mode**:\n```bash\npython3 scripts/fetch_transcripts.py --channel \"CHANNEL_URL\" --count 20 --output /tmp/transcripts.json\npython3 scripts/fetch_transcripts.py --video \"URL1\" --video \"URL2\"\n```\nOutput: JSON array with `id`, `title`, `upload_date`, `transcript`, `url` per video.\n\n**Additional context sourcing** — `summarize` also works on web pages, PDFs, and local files:\n```bash\nsummarize \"https://example.com/article\"\nsummarize --extract \"report.pdf\"\n```\n\n### Step 1b: Scan & Select (Channel Mode)\n\nWhen working from a channel with multiple transcripts:\n1. Scan titles, dates, and first ~500 chars of each\n2. Identify the strongest topic cluster\n3. Ask the user which thread to write about (or auto-select)\n4. Read full transcripts of only the selected 1-5 videos\n\n### Step 2: Analyze Source Material\n\nBefore writing, extract:\n- **Core thesis** — the single strongest argument or revelation\n- **Key data points** — statistics, quotes, dates, names that anchor credibility\n- **Narrative moments** — anecdotes, examples, scenes to dramatize\n- **Source links** — URLs, studies, references mentioned (verify independently)\n- **Missing context** — what does the reader need that the video assumed?\n\nFor multi-video posts: find the connective thread. Weave, don't summarize sequentially.\n\n### Step 3: Draft the Post\n\nLoad `references/voice-guide.md` for the author's writing style. If no voice guide exists, ask the user to describe their voice or provide 2-3 sample posts to analyze.\n\n#### Structure Template\n\n1. **Cold open (1-3 paragraphs):** Scene-setting. Specific time, place, sensory details. Emotional hook before data.\n2. **Thesis pivot (1 paragraph):** Connect scene to bigger story.\n3. **Data body (5-15 paragraphs):** Alternate data and editorial. Each stat gets a punch line. Inline links. Subheadings only for major breaks. Include `[CHART: description]` placeholders where visualization helps.\n4. **Callback (1-2 paragraphs):** Return to opening scene/metaphor.\n5. **Closing (3-6 short paragraphs):** Escalating fragments. Final hammer line. Moral clarity, not hedging.\n\n#### Writing Rules\n\n- Vary sentence length dramatically — long data sentences, then one-word punches\n- Em dashes for asides, not parentheses\n- Sentence fragments for emphasis\n- No bullet lists in the body — narrative flow only\n- Link sources inline, don't footnote\n- No \"in conclusion\" or \"to summarize\"\n- Strong metaphors > abstract language\n- Credit video sources naturally: \"As [Name] put it in a recent interview...\" with link\n- Target: 1,500-3,000 words (match source depth)\n\n### Step 4: Headlines\n\nGenerate 3-5 options with distinct strategies:\n- **Contrast/irony:** \"The Lights Are Still On, But Nobody's Shopping\"\n- **Revelation:** \"New Study Reveals WHY...\"\n- **Moral framing:** \"They Planned It: How...\"\n- **Anniversary/callback:** \"Six Years Ago Today...\"\n\nEach with a one-line subtitle stating the thesis. Let the user pick.\n\n### Step 5: Deliver as Markdown\n\n```markdown\n# Title\n> Subtitle\n\nBody paragraphs...\n\n---\n*Source: [Video Title](url)*\n```\n\nAll source links inline. Image placeholders as `[IMAGE: description]`.\n\n### Step 6: Post to Substack\n\nRead the Substack URL from `references/config.md`. Use OpenClaw browser automation:\n\n1. Open managed browser (`profile=\"openclaw\"`)\n2. Navigate to the editor URL (e.g. `https://YOURNAME.substack.com/publish/post`)\n3. Click the title field (first large text area at top), type the title\n4. Click the subtitle area (below title, lighter placeholder text), type the subtitle\n5. Click the body area (main content region below subtitle)\n6. Paste the post body — use clipboard: write markdown to a temp file, `pbcopy < /tmp/post.md`, then send Meta+v to the browser\n7. Substack auto-saves as draft within seconds\n\n**Known issues & workarounds:**\n- Em dashes (`—`) garble as `,Äî` during clipboard paste → find/replace after paste, or type content directly for short posts\n- Large posts may need a brief pause between paste and verification\n- Always verify the draft at `https://YOURNAME.substack.com/publish` before publishing\n\n**Publishing:** Author reviews and clicks Publish manually (recommended). For trusted pipelines, the Publish button can be automated too.\n\n## Customization\n\n### Voice Guide\n\nThe voice guide is what makes this skill produce YOUR writing, not generic AI slop. See `references/voice-guide-template.md` for the full template.\n\nKey sections to fill in:\n- **Tone** — How do you sound? (authoritative, casual, angry, witty, academic?)\n- **Structure** — What's your typical post structure?\n- **Sentence style** — Short and punchy? Long and flowing? Mixed?\n- **What you DON'T do** — Hedge language? Bullet lists? Both-sides-ism?\n- **Topics & themes** — What do you write about?\n- **Constraints** — Factual standards, audience expectations, things to avoid\n\n### Multiple Publications\n\nFor multiple Substacks, create separate configs and voice guides:\n```\nreferences/config-publication-a.md\nreferences/config-publication-b.md\nreferences/voice-publication-a.md\nreferences/voice-publication-b.md\n```\n\nTell the agent which publication when requesting a post.\n\nFile v1.0.0:_meta.json\n\n{\n  \"ownerId\": \"kn7axjxn3pz992e0fw2v62mngs80f166\",\n  \"slug\": \"yt-to-blog\",\n  \"version\": \"1.0.0\",\n  \"publishedAt\": 1770084087708\n}\n\nFile v1.0.0:references/config-template.md\n\n# Substack Configuration\n\nCopy this file to `config.md` and fill in your Substack details.\n\n- **URL:** https://xxxxx.substack.com\n- **Editor URL:** https://xxxxx.substack.com/publish/post\n- **Publication name:** [Your Publication Name]\n\nFile v1.0.0:references/voice-guide-template.md\n\n# Voice Guide — [Your Name]\n\nCopy this file to `voice-guide.md` and fill in your details. The more specific you are, the better the output matches your voice.\n\n**Fastest setup:** Paste 2-3 of your best existing posts and ask your agent:\n> \"Read these and build a voice guide for my writing style. Save to references/voice-guide.md\"\n\n---\n\n## Publication\n- **Substack:** xxxxx.substack.com\n- **Publication name:** [Your Publication Name]\n- **Audience:** [Who reads you? Demographics, interests, expertise level, political lean if relevant]\n- **Subscriber scale:** [Rough size — helps calibrate tone and assumptions]\n\n## Tone\n\nPick 2-3 that fit and describe what they mean for YOUR writing:\n\n- **Conversational authority** — smart friend explaining over drinks, not a lecturer\n- **Credible urgency** — passionate but grounded in evidence\n- **Dry wit** — humor through understatement, not punchlines\n- **Righteous anger** — moral clarity without screaming\n- **Academic rigor** — precise, hedged, citation-heavy\n- **Warm storyteller** — empathy-first, narrative-driven\n- **Provocateur** — deliberately challenges assumptions\n\nYour tone: [describe here]\n\n## Structure Pattern\n\nHow do your posts typically flow? Describe your go-to structure:\n\nExample:\n1. Cold open with a scene or anecdote\n2. Pivot to the bigger thesis\n3. Data/evidence with editorial commentary\n4. Callback to the opening\n5. Punchy closing with short paragraphs\n\nYour structure:\n1.\n2.\n3.\n4.\n5.\n\n## Sentence Style\n\n- **Length variation:** [dramatic swings between long and short? consistently medium? long and flowing?]\n- **Punctuation:** [em dashes? ellipses? semicolons? colons for lists?]\n- **Fragments:** [use sentence fragments for emphasis? e.g. \"Always has. Always will.\"]\n- **Direct address:** [talk to the reader? \"you\" or implied?]\n- **References:** [pop culture? academic? sports? everyday life? scripture?]\n- **Metaphor style:** [escalating? one central metaphor? sprinkled throughout?]\n\n## What You Do NOT Do\n\nOften more useful than what you do. Things that would make a post feel \"off\":\n\n- [ ] Corporate jargon or buzzwords\n- [ ] Hedge language (\"it could potentially suggest...\")\n- [ ] Bullet-point lists in the post body\n- [ ] \"In conclusion\" or \"to summarize\" transitions\n- [ ] Both-sides-ism when evidence is clear\n- [ ] Long academic footnotes (prefer inline links)\n- [ ] Sycophantic hedging\n- [ ] [Add your own...]\n\n## Topics & Themes\n\nWhat do you write about? List your lanes:\n\n-\n-\n-\n\n## Constraints\n\nHard rules the agent must follow:\n\n- [ ] Factually defensible — link sources, don't fabricate\n- [ ] No defamation or unsubstantiated claims about named individuals\n- [ ] [Audience-specific rules, e.g. \"maintain credibility with X audience\"]\n- [ ] [Content restrictions, e.g. \"avoid medical advice\"]\n- [ ] [Add your own...]\n\n## Sample Headlines\n\nList 5-10 of YOUR actual headlines so the agent matches the style:\n\n1.\n2.\n3.\n4.\n5.\n\nFile v1.0.0:references/voice-guide.md\n\n# CovidReason / Rational Ground — Justin Hart's Writing Voice\n\n## Publication\n- **Substack:** covidreason.substack.com (\"Rational Ground by Justin Hart\")\n- **Audience:** Conservative, COVID-skeptic, data-literate, politically engaged. Mix of professionals, parents, and policy skeptics.\n- **Subscriber base:** Large enough to drive 13K+ views on X promo posts\n\n## Voice Characteristics\n\n### Tone\n- **Credible urgency** — never tabloid hysteria\n- Righteous anger tempered by dry wit\n- Data-dense but narratively compelling\n- Conversational authority — writes like a smart friend explaining something over drinks, not a lecturer\n- Moral clarity without preachiness\n\n### Structure Pattern (The \"Lights Are Still On\" Template)\n1. **Cold open with a scene** — cinematic, specific, emotionally grounding (time, place, sensory details)\n2. **Pivot to thesis** — the scene isn't just a scene, it's a symptom of something bigger\n3. **Data barrage** — dense, linked, cited statistics that build the case. Each stat gets a one-line editorial punch\n4. **Rhetorical callbacks** — return to the opening scene or metaphor periodically to maintain narrative coherence\n5. **Escalating metaphors** — cockroaches in walls, planes losing altitude, overdue invoices\n6. **Punchy closing** — short paragraphs, sentence fragments, building to a final hammer line\n\n### Headline Style\n- Provocative but credible\n- Often uses contrast or irony: \"The Lights Are Still On, But Nobody's Shopping\"\n- Subtitle is the thesis statement in one sentence\n- Occasionally uses BREAKING: or imperative framing\n\n### Sentence Style\n- Varies sentence length dramatically (long data sentences, then one-word punches)\n- Em dashes for parenthetical asides\n- Sentence fragments for emphasis (\"Always has. Always will.\")\n- Direct address to reader implied but rarely explicit\n- Pop culture and everyday references to anchor abstract data\n\n### What He Does NOT Do\n- Corporate jargon or hedge language (\"it could potentially suggest...\")\n- Sycophantic hedging\n- Both-sides-ism when the data is clear\n- Long academic citations (uses inline links instead)\n- Bullet-point lists in the body (narrative flow > formatting)\n\n## Topics & Themes\n- COVID policy failures, lockdown harms, vaccine safety data\n- Government overreach and institutional credibility collapse\n- Economic data the mainstream isn't covering\n- Cultural commentary on media, tech, politics\n- Screwtape Letters-style satirical essays (ongoing series)\n- Occasional personal narrative\n\n## Key Constraints\n- Must be factually defensible — Justin's credibility depends on accuracy\n- Sources should be linked, not just cited\n- Charts/images embedded where data supports it\n- Avoid defamation or unsubstantiated claims about specific individuals\n- Strong opinion is fine; fabrication is not","readmeExcerpt":"Skill: YT-to-Blog Content Engine Owner: justinhartbiz Summary: Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread → vertical video clips via HeyGen AI avatar. One URL in, entire content suite out. Use when asked to: \"turn this video into content\", \"create a content suite from this YouTube video\", \"write a blog from this video\", \"repurpose this video\", or any video-to-multi","codeSnippets":[],"executableExamples":[{"language":"text","snippet":"YouTube URL\n  ↓\n① Transcript (summarize CLI)\n  ↓\n② Blog Draft (AI-written in your voice)\n  ↓\n③ Substack Publish (browser automation)\n  ↓\n④ X/Twitter Post (bird CLI)\n  ↓\n④b Facebook Group (optional reminder)\n  ↓\n⑤ Script Splitter (extract hook moments)\n  ↓\n⑥ HeyGen Videos (AI avatar vertical clips)\n  ↓\n⑦ Post-Processing (ffmpeg crop/scale)\n  ↓\n📁 Output Folder (blog.md, videos, tweet.txt, URLs)"},{"language":"bash","snippet":"bash skills/yt-content-engine/setup.sh"},{"language":"bash","snippet":"curl -s -H \"X-Api-Key: API_KEY_HERE\" https://api.heygen.com/v2/avatars | python3 -c \"import sys,json; d=json.load(sys.stdin); print('✅ API key works!' if 'data' in d else '❌ Invalid key')\""},{"language":"bash","snippet":"curl -s -H \"X-Api-Key: API_KEY_HERE\" https://api.heygen.com/v2/avatars | python3 -c \"import sys,json; d=json.load(sys.stdin); print('✅ API key works!' if 'data' in d else '❌ Invalid key')\""},{"language":"bash","snippet":"curl -s -H \"X-Api-Key: API_KEY\" https://api.heygen.com/v2/avatars | python3 -c \""},{"language":"bash","snippet":"curl -s -H \"X-Api-Key: API_KEY\" https://api.heygen.com/v2/avatars | python3 -c \"\nimport sys, json\ndata = json.load(sys.stdin)\nfor a in data.get('data', {}).get('avatars', []):\n    print(f\\\"  {a['avatar_id']} — {a.get('avatar_name', 'unnamed')}\\\")\n\""}],"parameters":null,"dependencies":[],"permissions":[],"extractedFiles":[{"path":"SKILL.md","content":"---\nname: yt-to-blog\ndescription: >\n  Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread →\n  vertical video clips via HeyGen AI avatar. One URL in, entire content suite out. Use when asked to:\n  \"turn this video into content\", \"create a content suite from this YouTube video\", \"write a blog from\n  this video\", \"repurpose this video\", or any video-to-multi-platform content request.\n  Can run the full pipeline or individual steps.\n---\n\n# YT-to-Blog Content Engine\n\nYouTube URL → blog post + Substack + tweets + vertical video clips. The whole content machine.\n\n## Pipeline Overview\n\n```\nYouTube URL\n  ↓\n① Transcript (summarize CLI)\n  ↓\n② Blog Draft (AI-written in your voice)\n  ↓\n③ Substack Publish (browser automation)\n  ↓\n④ X/Twitter Post (bird CLI)\n  ↓\n④b Facebook Group (optional reminder)\n  ↓\n⑤ Script Splitter (extract hook moments)\n  ↓\n⑥ HeyGen Videos (AI avatar vertical clips)\n  ↓\n⑦ Post-Processing (ffmpeg crop/scale)\n  ↓\n📁 Output Folder (blog.md, videos, tweet.txt, URLs)\n```\n\n**One URL in → Five platforms out.** Run the whole thing or any step individually.\n\n---\n\n## First-Time Setup Wizard\n\nWalk the user through this on first use. It takes ~10 minutes once, then never again.\n\n### Step 1: Check Dependencies\n\nRun the setup script to check what's installed:\n\n```bash\nbash skills/yt-content-engine/setup.sh\n```\n\nRequired CLIs:\n| Tool | Purpose | Install |\n|------|---------|---------|\n| `summarize` | YouTube transcript extraction | `brew install steipete/tap/summarize` |\n| `bird` | X/Twitter posting | `brew install steipete/tap/bird` |\n| `ffmpeg` | Video post-processing | `brew install ffmpeg` |\n| `curl` | API calls to HeyGen | Usually pre-installed on macOS |\n| `python3` | Helper scripts | Usually pre-installed on macOS |\n\nIf anything is missing, tell the user what to install and wait for confirmation.\n\n### Step 2: HeyGen API Key\n\n1. Tell the user: \"Go to https://app.heygen.com/settings — grab your API key from the API section.\"\n2. If they don't have a HeyGen account: \"Sign up at https://heygen.com — the free tier gives you a few credits to test with.\"\n3. Save the key to `config.json` (see config schema below).\n4. Test it:\n\n```bash\ncurl -s -H \"X-Api-Key: API_KEY_HERE\" https://api.heygen.com/v2/avatars | python3 -c \"import sys,json; d=json.load(sys.stdin); print('✅ API key works!' if 'data' in d else '❌ Invalid key')\"\n```\n\n### Step 3: HeyGen Avatar Setup\n\nTell the user:\n\n> \"For vertical video clips, you need a HeyGen avatar. Here's what matters:\n>\n> **Record in PORTRAIT mode** (hold your phone vertically). This is critical — if you record landscape, the avatar will be a small strip in the center of a 9:16 frame and we'll need to crop/scale it (which works but loses quality).\n>\n> Go to https://app.heygen.com/avatars → Create Instant Avatar → follow their recording guide. Stand in good lighting, look at camera, speak naturally for 2+ minutes.\n>\n> Once created, grab your Avatar ID from the avatar details page.\"\n\n"},{"path":"_meta.json","content":"{\n  \"ownerId\": \"kn7axjxn3pz992e0fw2v62mngs80f166\",\n  \"slug\": \"yt-to-blog\",\n  \"version\": \"2.0.0\",\n  \"publishedAt\": 1770332043686\n}"},{"path":"references/config-template.md","content":"# Substack Configuration\n\nCopy this file to `config.md` and fill in your Substack details.\n\n- **URL:** https://xxxxx.substack.com\n- **Editor URL:** https://xxxxx.substack.com/publish/post\n- **Publication name:** [Your Publication Name]"},{"path":"references/voice-guide-template.md","content":"# Voice Guide — [Your Name]\n\nCopy this file to `voice-guide.md` and fill in your details. The more specific you are, the better the output matches your voice.\n\n**Fastest setup:** Paste 2-3 of your best existing posts and ask your agent:\n> \"Read these and build a voice guide for my writing style. Save to references/voice-guide.md\"\n\n---\n\n## Publication\n- **Substack:** xxxxx.substack.com\n- **Publication name:** [Your Publication Name]\n- **Audience:** [Who reads you? Demographics, interests, expertise level, political lean if relevant]\n- **Subscriber scale:** [Rough size — helps calibrate tone and assumptions]\n\n## Tone\n\nPick 2-3 that fit and describe what they mean for YOUR writing:\n\n- **Conversational authority** — smart friend explaining over drinks, not a lecturer\n- **Credible urgency** — passionate but grounded in evidence\n- **Dry wit** — humor through understatement, not punchlines\n- **Righteous anger** — moral clarity without screaming\n- **Academic rigor** — precise, hedged, citation-heavy\n- **Warm storyteller** — empathy-first, narrative-driven\n- **Provocateur** — deliberately challenges assumptions\n\nYour tone: [describe here]\n\n## Structure Pattern\n\nHow do your posts typically flow? Describe your go-to structure:\n\nExample:\n1. Cold open with a scene or anecdote\n2. Pivot to the bigger thesis\n3. Data/evidence with editorial commentary\n4. Callback to the opening\n5. Punchy closing with short paragraphs\n\nYour structure:\n1.\n2.\n3.\n4.\n5.\n\n## Sentence Style\n\n- **Length variation:** [dramatic swings between long and short? consistently medium? long and flowing?]\n- **Punctuation:** [em dashes? ellipses? semicolons? colons for lists?]\n- **Fragments:** [use sentence fragments for emphasis? e.g. \"Always has. Always will.\"]\n- **Direct address:** [talk to the reader? \"you\" or implied?]\n- **References:** [pop culture? academic? sports? everyday life? scripture?]\n- **Metaphor style:** [escalating? one central metaphor? sprinkled throughout?]\n\n## What You Do NOT Do\n\nOften more useful than what you do. Things that would make a post feel \"off\":\n\n- [ ] Corporate jargon or buzzwords\n- [ ] Hedge language (\"it could potentially suggest...\")\n- [ ] Bullet-point lists in the post body\n- [ ] \"In conclusion\" or \"to summarize\" transitions\n- [ ] Both-sides-ism when evidence is clear\n- [ ] Long academic footnotes (prefer inline links)\n- [ ] Sycophantic hedging\n- [ ] [Add your own...]\n\n## Topics & Themes\n\nWhat do you write about? List your lanes:\n\n-\n-\n-\n\n## Constraints\n\nHard rules the agent must follow:\n\n- [ ] Factually defensible — link sources, don't fabricate\n- [ ] No defamation or unsubstantiated claims about named individuals\n- [ ] [Audience-specific rules, e.g. \"maintain credibility with X audience\"]\n- [ ] [Content restrictions, e.g. \"avoid medical advice\"]\n- [ ] [Add your own...]\n\n## Sample Headlines\n\nList 5-10 of YOUR actual headlines so the agent matches the style:\n\n1.\n2.\n3.\n4.\n5."},{"path":"references/voice-guide.md","content":"# CovidReason / Rational Ground — Justin Hart's Writing Voice\n\n## Publication\n- **Substack:** covidreason.substack.com (\"Rational Ground by Justin Hart\")\n- **Audience:** Conservative, COVID-skeptic, data-literate, politically engaged. Mix of professionals, parents, and policy skeptics.\n- **Subscriber base:** Large enough to drive 13K+ views on X promo posts\n\n## Voice Characteristics\n\n### Tone\n- **Credible urgency** — never tabloid hysteria\n- Righteous anger tempered by dry wit\n- Data-dense but narratively compelling\n- Conversational authority — writes like a smart friend explaining something over drinks, not a lecturer\n- Moral clarity without preachiness\n\n### Structure Pattern (The \"Lights Are Still On\" Template)\n1. **Cold open with a scene** — cinematic, specific, emotionally grounding (time, place, sensory details)\n2. **Pivot to thesis** — the scene isn't just a scene, it's a symptom of something bigger\n3. **Data barrage** — dense, linked, cited statistics that build the case. Each stat gets a one-line editorial punch\n4. **Rhetorical callbacks** — return to the opening scene or metaphor periodically to maintain narrative coherence\n5. **Escalating metaphors** — cockroaches in walls, planes losing altitude, overdue invoices\n6. **Punchy closing** — short paragraphs, sentence fragments, building to a final hammer line\n\n### Headline Style\n- Provocative but credible\n- Often uses contrast or irony: \"The Lights Are Still On, But Nobody's Shopping\"\n- Subtitle is the thesis statement in one sentence\n- Occasionally uses BREAKING: or imperative framing\n\n### Sentence Style\n- Varies sentence length dramatically (long data sentences, then one-word punches)\n- Em dashes for parenthetical asides\n- Sentence fragments for emphasis (\"Always has. Always will.\")\n- Direct address to reader implied but rarely explicit\n- Pop culture and everyday references to anchor abstract data\n\n### What He Does NOT Do\n- Corporate jargon or hedge language (\"it could potentially suggest...\")\n- Sycophantic hedging\n- Both-sides-ism when the data is clear\n- Long academic citations (uses inline links instead)\n- Bullet-point lists in the body (narrative flow > formatting)\n\n## Topics & Themes\n- COVID policy failures, lockdown harms, vaccine safety data\n- Government overreach and institutional credibility collapse\n- Economic data the mainstream isn't covering\n- Cultural commentary on media, tech, politics\n- Screwtape Letters-style satirical essays (ongoing series)\n- Occasional personal narrative\n\n## Key Constraints\n- Must be factually defensible — Justin's credibility depends on accuracy\n- Sources should be linked, not just cited\n- Charts/images embedded where data supports it\n- Avoid defamation or unsubstantiated claims about specific individuals\n- Strong opinion is fine; fabrication is not"}],"languages":[],"docsSourceLabel":"CLAWHUB","editorialOverview":"Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread → vertical video clips via HeyGen AI avatar. One URL in, entire content suite out. Use when asked to: \"turn this video into content\", \"create a content suite from this YouTube video\", \"write a blog from this video\", \"repurpose this video\", or any video-to-multi-platform content request. Can run the full pipeline or individual steps. Skill: YT-to-Blog Content Engine Owner: justinhartbiz Summary: Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X/Twitter thread → vertical video clips via HeyGen AI avatar. One URL in, entire content suite out. Use when asked to: \"turn this video into content\", \"create a content suite from this YouTube video\", \"write a blog from this video\", \"repurpose this video\", or any video-to-multi","editorialQuality":{"score":100,"threshold":65,"status":"ready","wordCount":1621,"uniquenessScore":51,"reasons":[]}},"media":{"evidence":{"source":"no-media","verified":false,"confidence":"low","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":"No screenshots, media assets, or demo links are available."},"primaryImageUrl":null,"mediaAssetCount":0,"assets":[],"demoUrl":null},"ownerResources":{"evidence":{"source":"unclaimed","verified":false,"confidence":"low","updatedAt":"2026-04-15T00:45:39.800Z","emptyReason":"This page has not been claimed by the agent owner."},"hasCustomPage":false,"customPageUpdatedAt":null,"customLinks":[],"structuredLinks":{"docsUrl":null,"demoUrl":null,"supportUrl":null,"pricingUrl":null,"statusUrl":null},"customPage":null},"relatedAgents":{"evidence":{"source":"protocol-neighbors","verified":false,"confidence":"medium","updatedAt":"2026-10-10T08:43:20.077Z","emptyReason":null},"items":[{"id":"8ebccd8e-3863-4187-8355-c3f14e1f9edf","entityType":"agent","canonicalPath":"/agent/iofficeai-aionui","slug":"iofficeai-aionui","name":"AionUi","description":"Free, local, open-source 24/7 Cowork app and OpenClaw for Gemini CLI, Claude Code, Codex, OpenCode, Qwen Code, Goose CLI, Auggie, and more | 🌟 Star if you like it!","url":"https://github.com/iOfficeAI/AionUi","homepage":"https://www.aionui.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-10-09T19:11:12.944Z","createdAt":"2026-02-25T03:38:16.584Z","downloads":null},{"id":"b917f68a-ebff-438e-84f8-3f4b2494c0bc","entityType":"agent","canonicalPath":"/agent/activepieces-activepieces","slug":"activepieces-activepieces","name":"activepieces","description":"AI Agents & MCPs & AI Workflow Automation • (~400 MCP servers for AI agents) • AI Automation / AI Agent with MCPs • AI Workflows & AI Agents • MCPs for AI Agents","url":"https://github.com/activepieces/activepieces","homepage":"https://www.activepieces.com","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-15T02:22:12.426Z","createdAt":"2026-02-25T03:38:12.412Z","downloads":null},{"id":"5cb26759-3a39-483f-94cf-276a98c13bb8","entityType":"agent","canonicalPath":"/agent/cherryhq-cherry-studio","slug":"cherryhq-cherry-studio","name":"cherry-studio","description":"AI productivity studio with smart chat, autonomous agents, and 300+ assistants. Unified access to frontier LLMs","url":"https://github.com/CherryHQ/cherry-studio","homepage":"https://cherry-ai.com","source":"GITHUB_REPOS","protocols":["MCP","OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-04-11T14:38:40.986Z","createdAt":"2026-02-25T03:38:19.379Z","downloads":null},{"id":"6f6582d0-5d76-4f0f-b81d-86520247950b","entityType":"agent","canonicalPath":"/agent/copilotkit-copilotkit","slug":"copilotkit-copilotkit","name":"CopilotKit","description":"The Frontend for Agents & Generative UI. React + Angular","url":"https://github.com/CopilotKit/CopilotKit","homepage":"https://docs.copilotkit.ai","source":"GITHUB_REPOS","protocols":["OPENCLAW"],"capabilities":[],"safetyScore":100,"overallRank":70,"updatedAt":"2026-03-25T09:50:57.846Z","createdAt":"2026-02-25T03:39:14.617Z","downloads":null}],"links":{"hub":"/agent","source":"/agent/source/clawhub","protocols":[{"label":"OpenClaw","href":"/agent/protocol/openclew"}]}}}