Install
$ agentstack add skill-nicolas-diez-md-to-podcast-md-to-podcast ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Markdown → hands-free audio (NotebookLM + TTS fallback)
Purpose
The user has a long deliverable (spec, review, analysis) and no screen time to read it. This skill converts it into audio that goes straight to the heart of the content, for listening while driving or walking.
This is not "generate a podcast". It is producing the most information-dense audio possible: thesis, evidence, decisions — no generic greetings, no jokes, no repeating the title, no equal time per section.
When to use
- The user explicitly asks for audio of a
.md(car, walk, commute). - The document is substantial (spec, analysis, review) — not a one-line note.
When NOT to use
- To convert to HTML / visualize → use an HTML-artifact skill instead.
- Documents with secrets/sensitive data that should not pass through an external service (NotebookLM) — force
--prepare-onlyor Path B (local TTS, never leaves the machine).
Inputs
- Path to the
.md(required). --emphasis(optional): what to prioritize if the default (thesis + evidence + decisions) doesn't fit.--prepare-only(optional): generate only the optimized script, produce no audio.- Language: detected from the document (don't ask).
Outputs
artifacts/notebooklm-audio//
script-optimized.md ← edited single-narrator version (base for Path C)
dialogue-script.md ← A/B interview script (base for Path B)
audio.mp3 (Path A) or audio.m4a (Path B/C) ← final result
log.md ← one line per run: date, path used (A/B/C), duration
The original .md is never modified.
Main flow
1. Find and read the document
Locate the file by the given path. Read it fully before touching anything.
2. Editorial optimization → script-optimized.md
Produce a reorganized version (not an aggressive summary) that identifies:
- the central thesis
- 3–7 fundamental ideas
- critical evidence and numbers
- cause–effect relationships and tradeoffs
- decisions, risks, open questions
- what is secondary and can be deprioritized (flagged, not deleted)
Hard rules:
- Never invent content. Never turn opinions into facts.
- Never drop nuance that changes the conclusion.
- Never prioritize a section just because it appears first.
- Never summarize aggressively where the reasoning is needed to understand the thesis.
Head the file with an editorial-instruction block (start straight at the thesis, avoid generic intro/outro, no equal time per section) — this block is the brief for NotebookLM (Path A) and the content base for both TTS paths (B and C).
If --prepare-only: stop here and report the script is ready to load manually.
2b. Interview script → dialogue-script.md (for Path B)
NotebookLM Deep Dive works not because it has "two voices" but because of a structural mechanism — replicate that, not the surface:
- Asymmetric roles.
A= interviewer/curious (asks, reacts, paraphrases).B= expert (answers with the real content). Not two experts chatting evenly. - Content is extracted via questions, not direct exposition. Each fundamental idea from
script-optimized.mdenters through a framing question fromA— natural "something important is coming" signposting, instead of a flat narrator. - Paraphrase for retention, not filler. After a dense answer from
B,Amay compress it to one short line before the next question — helps a listener who is driving. Never repeat a conclusion more than once. - Asymmetric turn lengths.
A's questions and reactions: one or two sentences.B's answers: whatever the idea needs for real density — no padding. - No filler. No greetings, no jokes, no "welcome to the episode", no excess personality.
Anever fakes confusion for drama — it asks what an attentive reader would ask.
File format (flat lines, one turn per line):
A: short question or reaction?
B: answer carrying the real content.
A: next question...
B: ...
Open with A asking directly for the central thesis (no intro), cover the 3–7 fundamental ideas in order of importance (not order of appearance), and close with A asking for implications/risks and B answering with the synthesis — no generic sign-off.
3. Path A — NotebookLM (real Deep Dive)
Uses notebooklm-py (MIT, actively maintained) via the notebooklm CLI, installed with uv tool install "notebooklm-py[browser]". It speaks Google's internal RPC API directly — Playwright is only used for the initial login, not per run, so it is far more robust than DOM scraping.
Run:
scripts/notebooklm_audio.sh "" [prompt-file]
The script does, in order: auth check → create notebook → source add (the optimized .md) → generate audio --format deep-dive --length long --wait --json with the prompt from references/notebooklm-audio-prompt.md → download audio --force on success.
It prints a single contract line to stdout that the skill must read to decide the next step:
STATUS:completed:→ done, no Path B needed.STATUS:not_authenticated→notebooklm loginmissing (see Install). Tell the user once, then fall back to Path B.STATUS:rate_limited→ daily quota exhausted (free: ~3 audios/day, Pro: ~20/day). Not a bug — a real Google limit. Fall back to Path B without alarm.STATUS:failed:→ genuine error (notebook, source, generation or download). Fall back to Path B.
Never fall back in total silence: the skill always tells the user which path was used and why, even when the end result (the audio) is the same.
4. Path B — local dialogue TTS (default fallback, two voices)
Run scripts/dialogue_tts.py dialogue-script.md audio.m4a --voice-a "" --voice-b "". It synthesizes each turn with macOS say, concatenates with a short silence between turns (wave, no ffmpeg) and exports to m4a with afconvert. No network, no API key, no account or quota risk.
Tested voice pairs per language live in references/voice-choice.md — pick per the language detected in step 1.
4b. Path C — simple narration (emergency floor, one voice)
If dialogue_tts.py fails (malformed script, synthesis error) or the document is too short to justify the interview format: scripts/tts_fallback.sh script-optimized.md audio.m4a [voice]. One voice, same no-network mechanism. This is the floor — it must never fail.
5. Log and deliver
Append (never overwrite) one line to log.md: date, source file, path used (A, B or C) and reason if A was skipped, duration, audio path. Re-running the skill on the same document must not destroy previous runs — intermediate .mds and the audio are overwritten (latest result), but log.md accumulates history. Never write tokens, cookies or credentials here.
Install (one-time)
brew install uv
uv tool install "notebooklm-py[browser]"
"$(uv tool dir)/notebooklm-py/bin/playwright" install chromium
The third command downloads the Chromium that Playwright uses only for login (it does not touch your real Chrome) — without it, notebooklm login fails with "Executable doesn't exist".
Then, a manual user action (the skill cannot do it — it is a real Google login):
export PATH="$HOME/.local/bin:$PATH"
notebooklm login
This opens a browser, the user signs in, and the session persists in ~/.notebooklm/profiles/default/storage_state.json (outside the project, never versioned). Verify with notebooklm doctor.
Security rules
- The Google session persists locally only, outside git — add the session folder to the project's
.gitignore. - Never print cookies/tokens/passwords to the chat, to
log.mdor to any versioned file. - Manual Google login only when unavoidable. Never bypass 2FA/CAPTCHA.
- Path A depends on an unofficial Google interface: it can break with UI changes, and automating a personal account is not a supported flow — a consciously accepted risk of the hybrid architecture.
Usage examples
Turn projects/x/spec.md into audio for the car.
Make me audio of this deliverable, prioritizing decisions and risks.
Prepare this MD for NotebookLM but don't generate the audio yet.
References
- [notebooklm-audio-prompt.md](references/notebooklm-audio-prompt.md) — instructions for Path A.
- [voice-choice.md](references/voice-choice.md) — tested voice pairs for Path B (dialogue).
Troubleshooting
notebooklm loginnever detects the login ("Login not detected within 5 minutes") → known blocker, not our bug. Google renamed NotebookLM and moved the domain (July 2026);notebooklm-pysupport may lag behind in published releases — check the repo's issues/changelog before retrying. Path B delivers the audio meanwhile.STATUS:not_authenticated→ runnotebooklm loginmanually (see Install). The skill still delivers audio via Path B meanwhile.STATUS:rate_limited→ daily quota exhausted (~3/day free, ~20/day Pro). Not an error to fix — wait a day or upgrade. Path B has no such limit.- "say: command not found" /
afconvertfails → these are macOS binaries; confirm you're on macOS, not Linux/CI. - Path B/C audio sounds odd / clips words → check the script for code blocks, tables or raw markdown that wasn't cleaned before
say. dialogue_tts.pyfails or the script lacks well-formedA:/B:turns → drop to Path C (one voice) automatically, tell the user. Never block audio delivery on this.notebooklmbreaks after a NotebookLM/Google update → it is an unofficial internal API; it happens. Pin the version (no blind--upgrade) and read the repo changelog before updating.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: nicolas-diez
- Source: nicolas-diez/md-to-podcast
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.