| name | podcast-digest |
| description | Generate a weekly podcast (script + rendered mp3) from the code-intel-digest mirror. Configurable length (default 30 min). Use when Stephanie says 'make me a podcast', 'generate this week's podcast', 'render the digest as audio', etc. Produces both the markdown script and the mp3 in out/. Requires the code-intel-copilot MCP server. |
| allowed-tools | mcp__code-intel-copilot__search_items, mcp__code-intel-copilot__semantic_search_items, mcp__code-intel-copilot__get_item, mcp__code-intel-copilot__aggregate_items, mcp__code-intel-copilot__mirror_status, Read, Write, Bash, AskUserQuestion |
Podcast Digest
End-to-end weekly podcast workflow. Writes a markdown script targeted at a configurable length, then renders it to mp3 via the project's TTS pipeline.
Args parsing
The skill accepts a free-form args string with optional key=value flags. Parse leniently โ unknown text is treated as a lens hint.
| Flag | Default | Notes |
|---|
length=<N>min or length=<N> | 30min | Spoken runtime target. Drives word count (โ150 wpm) and segment count. |
provider=openai|elevenlabs|nemo | openai | TTS provider for the render. ElevenLabs is higher quality but ~10ร cost. |
voice=<voice> | alloy | OpenAI: alloy/echo/fable/onyx/nova/shimmer. ElevenLabs: voice id. |
audience=personal|share|general | general | Personal = orient-style (terse, Stephanie-context). Share = distill-style (polished). General = neutral default. |
since=YYYY-MM-DD | todayโ7 | Coverage window start. |
skip-render=true | false | Write script only, don't render audio. |
If the args string is empty, default to length=30min, audience=general, and proceed.
Always do first
- Call
mirror_status exactly once and check dbMode. In direct mode the server reads the live production database (real-time) โ note "Data: live production (real-time)" in the script header and continue. In mirror mode, if staleMinutes > 90 warn the user and ask whether to proceed (the data is stale; the podcast will reflect old news).
- Compute target word count:
length_minutes * 150. So 30 min โ 4500 words. 20 min โ 3000. 45 min โ 6750.
- Compute segment count: roughly
ceil(length_minutes / 5). 30 min โ 6 segments. 20 min โ 4. 45 min โ 9.
- Compute
since: ISO date 7 days ago unless overridden.
Recipe
Phase 1 โ Gather
Same shape as digest-insights/SKILL.md Recipe 1, parameterized:
search_items(since=<since>, limit=50) โ recency sweep.
aggregate_items(group_by='source', since=<since>, limit=10) โ coverage drivers.
aggregate_items(group_by='category', since=<since>, limit=9) โ drop dead categories.
- Pull high-signal categories (avg final score > 0.3):
search_items(category=<X>, since=<since>, limit=25) for each of product_news, ai_news, ai_dev, tech_articles. Skip categories with avgFinalScore < 0.15 (they're noise).
- For each thematic hypothesis you form (3โ5 themes is right for 30 min), run
semantic_search_items(query=<theme>, limit=8) to find corroboration.
- For the 8โ12 strongest items across themes,
get_item to pull substance.
Phase 2 โ Cluster
Group items into thematic segments. Rules:
- Number of segments = the value computed in step 3 above (
ceil(length / 5)).
- Words per segment = total word target / segments, minus ~150 for intro+outro. So 30 min โ ~700 words/segment for 6 segments.
- Each segment must be corroborated: at least 2 items from different sources making the same point. A single item โ a trend; either fold it into another segment or drop it.
- Order segments by causal arc, not chronologically. What's the through-line? Pick segment 1 to set up segment 2, etc.
- Include at least one dissenting/contrarian item per segment when the data has one. The script should not feel like an industry press release.
Phase 3 โ Write the script
Write to out/podcast-script-YYYY-MM-DD.md with this structure:
# Code Intel Digest โ Weekly Podcast Script
**Episode date:** YYYY-MM-DD
**Coverage window:** YYYY-MM-DD โ YYYY-MM-DD
**Data through:** <mirror_status.lastSyncedAt>
**Target runtime:** ~N minutes (~<word_target> words spoken)
**Segments:** <n> + cold open + outro
---
## COLD OPEN (~30s, ~75 words)
<framing โ what the through-line is, why this week matters>
---
## SEGMENT 1 โ <punchy title> (โM min)
<โ700 words: claim โ 2-4 corroborating items with URLs โ contrarian/dissent โ action>
---
[... segments 2..n ...]
---
## OUTRO (~30s, ~75 words)
<recap arc, name 2-3 things to watch next week, one concrete action>
---
## Citations
| # | Title | Source | URL |
|---|---|---|---|
[every cited item โ reader can verify]
---
*Generated by `podcast-digest` skill from the code-intel-digest local mirror. Mirror freshness: <ts>. ~<words> words / ~<minutes> min spoken runtime.*
Voice & tone:
- Conversational. Contractions. Spoken cadence ("here's what I'd watch" not "the following items merit observation").
- No emoji or markdown formatting decorations within segment prose โ they trip TTS sanitization.
- Numbers spelled out only when ambiguity would help comprehension ("Five hundred and seventy thousand dollars" sounds better than "$570K" when read aloud โ or the renderer will say "five seventy K" which is fine; trust the sanitizer for currency, but write out years and large round numbers).
- Cite people and publications by name in prose ("Simon Willison wrote..." not just "[2]").
- Each segment ends with a concrete action the listener could take.
Phase 4 โ Render audio
Unless skip-render=true:
npx tsx scripts/render-podcast.ts \
--input out/podcast-script-YYYY-MM-DD.md \
--provider <provider> \
--voice <voice>
This writes out/podcast-YYYY-MM-DD.mp3 and prints the path.
The script handles:
- Stripping markdown (frontmatter, headings, segment markers, citations table) to plain prose.
- Calling
renderAudio() directly from src/lib/audio/render.ts (no dev server, no auth).
- Chunking transcripts > 3800 chars (OpenAI tts-1 limit) and stitching the resulting mp3 buffers.
- Writing the mp3 + reporting word count, render time, and audio duration.
Phase 5 โ Report back
Print to the user:
- Script path:
out/podcast-script-YYYY-MM-DD.md
- Audio path:
out/podcast-YYYY-MM-DD.mp3 (and reported duration)
- Bullet summary of the segments (one line each)
- Any items dropped because they were singletons (so the user can override if they think you missed something)
Cost guidance
For OpenAI tts-1 at ~$0.015 per 1k chars:
- 20 min (3000 words โ 18k chars) โ $0.27
- 30 min (4500 words โ 27k chars) โ $0.41
- 45 min (6750 words โ 40k chars) โ $0.60
For ElevenLabs (varies by tier):
- 30 min โ $3โ5 typical
NeMo: free if a NeMo TTS server is running locally; check before assuming it works.
If provider=elevenlabs is requested, surface the cost estimate and confirm before rendering.
Guardrails
- Don't fabricate items. Every claim in the script must trace to an
items row. Use Citations table to enforce this on yourself.
- Don't enumerate raw tool output. Synthesize.
- Respect the lens. If
audience=personal, write as if for Stephanie's catch-up โ denser, less context-setting. If audience=share, polish and make every sentence forwardable.
- Mirror staleness > 90 min โ warn loudly, don't silently produce an episode about old news.
- If the week has < ~25 substantive items, tell the user and ask whether to widen the window. Padding a thin week produces a bad podcast.
- Don't render audio without writing the script first. The script is the artifact the user can edit; the mp3 is downstream.
Related skills
digest-insights โ general Q&A over the same mirror. Use when the question isn't "make me a podcast" โ e.g. "what's trending in dev tools this month".
orient-digest โ personal-context catch-up, terse. Output is text, not audio.
distill-digest โ share-ready text digest for Slack/email. Output is text, not audio.