[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6N5GGZ2HSS6D7RYF3WFP8XS":3,"skill-same:01M6N5GGZ2HSS6D7RYF3WFP8XS":110},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":30,"publishedAt":51,"linkOnly":52,"markdownContent":53,"markdownBody":54,"frontMatter":55,"refName":56,"commitSha":57,"skillDirectory":58,"forksCount":59,"repositoryHtmlUrl":60,"repositoryHomepageUrl":6,"ownerHtmlUrl":61,"ownerBlogUrl":6,"ownerDescription":62,"updatedAt":63,"install":64,"resources":77},"01M6N5GGZ2HSS6D7RYF3WFP8XS","yt-to-blog",null,"Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X\u002FTwitter 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.","Kernel8901\u002Fai-agent-skills-classification","Kernel8901","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F72647553?v=4","01M6KV388J2H5PNC8D8P940BJD","01M6KV38CS5B3AY63RKZB410YM",7,"MIT","https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002FSKILL.md",[17,22,25],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",{"categoryNo":23,"categoryName":24,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":26,"categoryName":27,"taxonomyType":28,"taxonomyTypeDesc":29},"01JBSEEDCAT000000000000036","内容创作者","PROFESSION","适用职业",[31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50],"blog","yt","yt to blog","youtube","content pipeline","suite","request","heygen","URL","platform","full","create","use","write","twitter","blog post","Video","pipeline","transcript","run","2026-11-10 10:12:30",false,"---\nname: yt-to-blog\ndescription: >\n  Full content pipeline: YouTube URL → transcript → blog post → Substack draft → X\u002FTwitter 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\u002FTwitter 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\u002Fscale)\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\u002Fyt-content-engine\u002Fsetup.sh\n```\n\nRequired CLIs:\n| Tool | Purpose | Install |\n|------|---------|---------|\n| `summarize` | YouTube transcript extraction | `brew install steipete\u002Ftap\u002Fsummarize` |\n| `bird` | X\u002FTwitter posting | `brew install steipete\u002Ftap\u002Fbird` |\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:\u002F\u002Fapp.heygen.com\u002Fsettings — grab your API key from the API section.\"\n2. If they don't have a HeyGen account: \"Sign up at https:\u002F\u002Fheygen.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:\u002F\u002Fapi.heygen.com\u002Fv2\u002Favatars | 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\u002Fscale it (which works but loses quality).\n>\n> Go to https:\u002F\u002Fapp.heygen.com\u002Favatars → 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:\u002F\u002Fapi.heygen.com\u002Fv2\u002Favatars | 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:\u002F\u002Fapp.heygen.com\u002Fvoice-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:\u002F\u002Fapi.heygen.com\u002Fv2\u002Fvoices | 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:\u002F\u002Fsubstack.com\u002Fsign-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\u002Fyt-content-engine\u002Fconfig.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\u002Fyt-to-blog\u002Freferences\u002Fvoice-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\u002Fyt-content-engine\u002Fsetup.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:\u002F\u002Fyoutu.be\u002FXXXXX\"\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 \u002Ftmp\u002Fyt-content-engine\u002Foutput-$(date +%Y-%m-%d)\u002Fscripts\nmkdir -p \u002Ftmp\u002Fyt-content-engine\u002Foutput-$(date +%Y-%m-%d)\u002Fvideos\n```\n\n```bash\nsummarize \"YOUTUBE_URL\" --extract > \u002Ftmp\u002Fyt-content-engine\u002Ftranscript.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\u002Fyt-to-blog\u002Freferences\u002Fvoice-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\u002Fmetaphor.\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\u002Firony, 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:\u002F\u002FPUBLICATION.substack.com\u002Fpublish\u002Fpost`\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 \u003C \u002Ftmp\u002Fpost.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\u002Freplace after paste\n- Large posts: pause briefly between paste and verification\n- Verify draft at `https:\u002F\u002FPUBLICATION.substack.com\u002Fpublish`\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\u002Fsubstack-url.txt`.\n\n### Step ④: X\u002FTwitter Post\n\nCompose and post using the `bird` CLI.\n\n**Compose the tweet\u002Fthread:**\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\u002Ftweet.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\u002Ffacebook-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\u002FControversy** — 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\u002Fscripts\u002Fclip-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:\u002F\u002Fapi.heygen.com\u002Fv2\u002Fvideo\u002Fgenerate\" \\\n  -H \"X-Api-Key: $API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\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\u002Fscripts\u002Fclip-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:\u002F\u002Fapi.heygen.com\u002Fv1\u002Fvideo_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\u002Fvideos\u002Fclip-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 \u002Ftmp\u002Fframe.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)\u002F2\" \\\n  -c:a copy \\\n  -y output.mp4\n```\n\n**Alternative manual detection** (preferred — cropdetect often fails when background is white\u002Flight):\n\nHeyGen typically renders landscape avatars centered on a white\u002Flight 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 \u002Ftmp\u002Fframe.png 2>\u002Fdev\u002Fnull\n\nffmpeg -y -i \u002Ftmp\u002Fframe.png -vf \"crop=1:ih:iw\u002F2:0,format=gray\" -f rawvideo -pix_fmt gray - 2>\u002Fdev\u002Fnull | \\\n  python3 -c \"\nimport sys\ndata = sys.stdin.buffer.read()\nfirst = last = None\nfor i, b in enumerate(data):\n    if b \u003C 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) \u002F\u002F 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)\u002F2: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\u002Fvideos\u002Fclip-1.mp4`, `clip-2.mp4`, etc.\n\nIf crop mode is `portrait`, just copy the raw files:\n```bash\ncp output\u002Fvideos\u002Fclip-1-raw.mp4 output\u002Fvideos\u002Fclip-1.mp4\n```\n\n### Step ⑧: Output\n\nOrganize everything in a dated output folder:\n\n```\noutput-YYYY-MM-DD\u002F\n├── blog.md              # Final blog post\n├── tweet.txt            # Tweet text (posted or ready to post)\n├── substack-url.txt     # URL of Substack draft\u002Fpost\n├── scripts\u002F\n│   ├── clip-1.txt       # Spoken word scripts\n│   ├── clip-2.txt\n│   └── ...\n├── videos\u002F\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:\u002F\u002Fyoutu.be\u002FXXXXX\",\n  \"date\": \"2026-02-03\",\n  \"blog\": \"blog.md\",\n  \"substackUrl\": \"https:\u002F\u002F...\",\n  \"tweetUrl\": \"https:\u002F\u002F...\",\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\u002Fpublished)\n- ✅ Tweet: posted \u002F 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\u002Fyt-content-engine\u002Fconfig.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\u002FTwitter 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:\u002F\u002Fapp.heygen.com\u002Fsettings — 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","# 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\u002FTwitter 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\u002Fscale)\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\u002Fyt-content-engine\u002Fsetup.sh\n```\n\nRequired CLIs:\n| Tool | Purpose | Install |\n|------|---------|---------|\n| `summarize` | YouTube transcript extraction | `brew install steipete\u002Ftap\u002Fsummarize` |\n| `bird` | X\u002FTwitter posting | `brew install steipete\u002Ftap\u002Fbird` |\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:\u002F\u002Fapp.heygen.com\u002Fsettings — grab your API key from the API section.\"\n2. If they don't have a HeyGen account: \"Sign up at https:\u002F\u002Fheygen.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:\u002F\u002Fapi.heygen.com\u002Fv2\u002Favatars | 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\u002Fscale it (which works but loses quality).\n>\n> Go to https:\u002F\u002Fapp.heygen.com\u002Favatars → 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:\u002F\u002Fapi.heygen.com\u002Fv2\u002Favatars | 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:\u002F\u002Fapp.heygen.com\u002Fvoice-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:\u002F\u002Fapi.heygen.com\u002Fv2\u002Fvoices | 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:\u002F\u002Fsubstack.com\u002Fsign-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\u002Fyt-content-engine\u002Fconfig.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\u002Fyt-to-blog\u002Freferences\u002Fvoice-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\u002Fyt-content-engine\u002Fsetup.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:\u002F\u002Fyoutu.be\u002FXXXXX\"\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 \u002Ftmp\u002Fyt-content-engine\u002Foutput-$(date +%Y-%m-%d)\u002Fscripts\nmkdir -p \u002Ftmp\u002Fyt-content-engine\u002Foutput-$(date +%Y-%m-%d)\u002Fvideos\n```\n\n```bash\nsummarize \"YOUTUBE_URL\" --extract > \u002Ftmp\u002Fyt-content-engine\u002Ftranscript.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\u002Fyt-to-blog\u002Freferences\u002Fvoice-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\u002Fmetaphor.\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\u002Firony, 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:\u002F\u002FPUBLICATION.substack.com\u002Fpublish\u002Fpost`\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 \u003C \u002Ftmp\u002Fpost.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\u002Freplace after paste\n- Large posts: pause briefly between paste and verification\n- Verify draft at `https:\u002F\u002FPUBLICATION.substack.com\u002Fpublish`\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\u002Fsubstack-url.txt`.\n\n### Step ④: X\u002FTwitter Post\n\nCompose and post using the `bird` CLI.\n\n**Compose the tweet\u002Fthread:**\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\u002Ftweet.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\u002Ffacebook-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\u002FControversy** — 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\u002Fscripts\u002Fclip-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:\u002F\u002Fapi.heygen.com\u002Fv2\u002Fvideo\u002Fgenerate\" \\\n  -H \"X-Api-Key: $API_KEY\" \\\n  -H \"Content-Type: application\u002Fjson\" \\\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\u002Fscripts\u002Fclip-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:\u002F\u002Fapi.heygen.com\u002Fv1\u002Fvideo_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\u002Fvideos\u002Fclip-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 \u002Ftmp\u002Fframe.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)\u002F2\" \\\n  -c:a copy \\\n  -y output.mp4\n```\n\n**Alternative manual detection** (preferred — cropdetect often fails when background is white\u002Flight):\n\nHeyGen typically renders landscape avatars centered on a white\u002Flight 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 \u002Ftmp\u002Fframe.png 2>\u002Fdev\u002Fnull\n\nffmpeg -y -i \u002Ftmp\u002Fframe.png -vf \"crop=1:ih:iw\u002F2:0,format=gray\" -f rawvideo -pix_fmt gray - 2>\u002Fdev\u002Fnull | \\\n  python3 -c \"\nimport sys\ndata = sys.stdin.buffer.read()\nfirst = last = None\nfor i, b in enumerate(data):\n    if b \u003C 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) \u002F\u002F 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)\u002F2: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\u002Fvideos\u002Fclip-1.mp4`, `clip-2.mp4`, etc.\n\nIf crop mode is `portrait`, just copy the raw files:\n```bash\ncp output\u002Fvideos\u002Fclip-1-raw.mp4 output\u002Fvideos\u002Fclip-1.mp4\n```\n\n### Step ⑧: Output\n\nOrganize everything in a dated output folder:\n\n```\noutput-YYYY-MM-DD\u002F\n├── blog.md              # Final blog post\n├── tweet.txt            # Tweet text (posted or ready to post)\n├── substack-url.txt     # URL of Substack draft\u002Fpost\n├── scripts\u002F\n│   ├── clip-1.txt       # Spoken word scripts\n│   ├── clip-2.txt\n│   └── ...\n├── videos\u002F\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:\u002F\u002Fyoutu.be\u002FXXXXX\",\n  \"date\": \"2026-02-03\",\n  \"blog\": \"blog.md\",\n  \"substackUrl\": \"https:\u002F\u002F...\",\n  \"tweetUrl\": \"https:\u002F\u002F...\",\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\u002Fpublished)\n- ✅ Tweet: posted \u002F 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\u002Fyt-content-engine\u002Fconfig.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\u002FTwitter 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:\u002F\u002Fapp.heygen.com\u002Fsettings — 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",{"name":5,"description":7},"main","f4cb2d49f7667e79ef7804f50396d5aaffdf1f56","内容创意\u002Fyt-to-blog",1,"https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification","https:\u002F\u002Fgithub.com\u002FKernel8901","a little little little dobe in life but science geek","2026-04-05 16:43:56",{"source":8,"skillName":5,"commands":65,"prompt":75,"zipAvailable":76},[66,69,72],{"tool":67,"command":68},"npx","npx skills add Kernel8901\u002Fai-agent-skills-classification --skill yt-to-blog",{"tool":70,"command":71},"bunx","bunx skills add Kernel8901\u002Fai-agent-skills-classification --skill yt-to-blog",{"tool":73,"command":74},"pnpm","pnpm dlx skills add Kernel8901\u002Fai-agent-skills-classification --skill yt-to-blog","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002FSKILL.md",true,[78,84,88,92,96,100,106],{"path":79,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":82,"sourceHtmlUrl":83},"_meta.json","REFERENCE","参考文档",472,"https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002F_meta.json",{"path":85,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":86,"sourceHtmlUrl":87},"config.json",782,"https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002Fconfig.json",{"path":89,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":90,"sourceHtmlUrl":91},"references\u002Fconfig-template.md",236,"https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002Freferences\u002Fconfig-template.md",{"path":93,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":94,"sourceHtmlUrl":95},"references\u002Fvoice-guide-template.md",2931,"https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002Freferences\u002Fvoice-guide-template.md",{"path":97,"resourceType":80,"resourceTypeDesc":81,"sizeBytes":98,"sourceHtmlUrl":99},"references\u002Fvoice-guide.md",2816,"https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002Freferences\u002Fvoice-guide.md",{"path":101,"resourceType":102,"resourceTypeDesc":103,"sizeBytes":104,"sourceHtmlUrl":105},"scripts\u002Ffetch_transcripts.py","SCRIPT","脚本",4897,"https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002Fscripts\u002Ffetch_transcripts.py",{"path":107,"resourceType":102,"resourceTypeDesc":103,"sizeBytes":108,"sourceHtmlUrl":109},"setup.sh",6809,"https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F内容创意\u002Fyt-to-blog\u002Fsetup.sh",[111,134,166,177,187,217],{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":117,"publishedAt":51},"01M6Q5R6WZ06RRB9RQTW54B34B","zyfai","Earn yield on any Ethereum wallet on Base, Arbitrum, and Plasma. Use when a user wants passive DeFi yield on their funds. Deploys a non-custodial subaccount (Safe) linked to their EOA, enables automated yield optimization, and lets them deposit\u002Fwithdraw anytime.","https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F金融\u002Fzyfai-sdk\u002FSKILL.md",[],[113,118,119,120,121,122,43,123,124,125,126,127,128,129,130,131,132,133],"safe","ethereum","defi","Optimization","user","Wallet","base","deposit","withdraw","yield","arbitrum","non-custodial","passive","defi yield","earn","yield optimization",{"skillNo":135,"name":136,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":137,"categories":138,"keywords":148,"publishedAt":51},"01M6Q5R337B5JSNER4P4YS03TN","zerion-api","https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F金融\u002Fzerion-api\u002FSKILL.md",[139,142,145],{"categoryNo":140,"categoryName":141,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":143,"categoryName":144,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":146,"categoryName":147,"taxonomyType":28,"taxonomyTypeDesc":29},"01JBSEEDCAT000000000000030","开发工程师",[149,150,151,152,153,154,155,120,156,123,157,158,159,160,161,162,163,164,165],"api","zerion api","zerion","MCP","mcp server","server","blockchain","transactions","crypto wallet","crypto","evm","solana","query","across","positions","token","prices",{"skillNo":167,"name":168,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":169,"categories":170,"keywords":174,"publishedAt":51},"01M6Q5QZFYTDAZMZ27BN5VZXAR","zerion-api-skill","https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F金融\u002Fzerion-api-skill\u002FSKILL.md",[171,172,173],{"categoryNo":140,"categoryName":141,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":143,"categoryName":144,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":146,"categoryName":147,"taxonomyType":28,"taxonomyTypeDesc":29},[149,175,176,150,151,152,153,154,155,120,156,123,158,159,160,161,162,163,164,165],"api skill","zerion api skill",{"skillNo":178,"name":179,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":180,"categories":181,"keywords":185,"publishedAt":51},"01M6Q5QW17QW98RA3FE45E4RD8","zerion-api-mcp","https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F金融\u002Fzerion-api-mcp\u002FSKILL.md",[182,183,184],{"categoryNo":140,"categoryName":141,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":143,"categoryName":144,"taxonomyType":20,"taxonomyTypeDesc":21},{"categoryNo":146,"categoryName":147,"taxonomyType":28,"taxonomyTypeDesc":29},[152,149,186,150,151,153,154,120,156,123,157,158,159,160,161,162,163,164,165],"zerion api mcp",{"skillNo":188,"name":189,"nameZh":6,"description":190,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":191,"categories":192,"keywords":196,"publishedAt":51},"01M6Q5QQYZB7A501BDEQWKGSJT","yahoo-finance-forex","Fetches real-time FOREX news and market data from Yahoo Finance for major currency pairs (EUR\u002FUSD, GBP\u002FUSD, USD\u002FJPY, etc.). Analyzes sentiment and provides trading context.","https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F金融\u002Fyahoo-finance-forex\u002FSKILL.md",[193],{"categoryNo":194,"categoryName":195,"taxonomyType":28,"taxonomyTypeDesc":29},"01JBSEEDCAT000000000000040","财务",[197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216],"finance","yahoo finance","forex","yahoo","yahoo finance forex","github","https","sentiment","real-time","python3","install","Market","usd","news","time","currency","gbp","pip","market data","for",{"skillNo":218,"name":219,"nameZh":6,"description":220,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":221,"categories":222,"keywords":224,"publishedAt":51},"01M6Q5QM6VS84WZVM7BKXXPF32","yahoo-finance","This skill should be used when the user asks to \"get stock prices\", \"check stock quotes\", \"look up earnings\", \"get financial data\", \"find trending stocks\", or needs stock market data from Yahoo Finance.","https:\u002F\u002Fgithub.com\u002FKernel8901\u002Fai-agent-skills-classification\u002Fblob\u002Fmain\u002F金融\u002Fyahoo-finance-cli\u002FSKILL.md",[223],{"categoryNo":194,"categoryName":195,"taxonomyType":28,"taxonomyTypeDesc":29},[197,198,200,225,226,227,228,229,230,208,231,232,233,234,235,236,237,238,165,215],"cli","homebrew","find","check","brew","up","local","get","stock","link","trending","sf","quotes","ln"]