| name | ghostwriter-one-off-podcast |
| description | Create ad hoc Ghostwriter podcast episodes from user-provided documents, article URLs, notes, or extracted text, then poll until the episode is ready in the private podcast feed. |
| version | 1.0.0 |
| author | Hermes Agent |
| metadata | {"hermes":{"tags":["ghostwriter","podcast","openclaw","documents","audio"],"config":[{"key":"ghostwriter.base_url","description":"HTTPS base URL for Ghostwriter without the /api suffix","prompt":"Ghostwriter base URL"},{"key":"ghostwriter.token_env","description":"Environment variable containing the Ghostwriter API token or JWT","default":"GHOSTWRITER_TOKEN","prompt":"Ghostwriter token environment variable"}]}} |
Ghostwriter One-Off Podcast
Use this skill when the user asks to turn one or more documents, notes, article URLs, or research sources into a one-off Ghostwriter podcast episode.
Requirements
- Ghostwriter endpoint:
<GHOSTWRITER_BASE_URL>/api/podcast/episodes/one-off
- Authentication:
Authorization: Bearer <token>
- Base URL source: prefer
$GHOSTWRITER_BASE_URL, then $GW_BASE, then --base-url. Use HTTPS for remote hosts. The helper only allows plain HTTP for loopback hosts unless --allow-insecure-http is explicitly passed.
- Token source: prefer
$GHOSTWRITER_TOKEN, then $GW_TOKEN, then a --env-file such as ~/.env; do not ask the user to paste secrets into chat if an environment variable or env file can be used.
- Phase 1 accepts only:
type: "url" for reachable article URLs
type: "text" for pasted or extracted document text
- Limits: 1-20 sources, each text source under 120000 characters, each source should have at least about 80 characters of usable content.
Workflow
- Collect the requested source material.
- For Obsidian notes, pass note/folder paths to the helper instead of manually copying Markdown.
- Preview source counts and warnings before submitting when the request has multiple notes or linked-note expansion.
- Create the episode with
scripts/create_one_off_podcast.py.
- Poll until status is
ready or failed.
- Download the audio when the user asked for a file or automation output.
- Report the episode id, final status, source count, downloaded path, and feed/download URL if ready. If failed, report
error_message.
Helper Script
The script automatically reads ~/.env when present, unless --no-default-env-files is passed. This handles the common case where source ~/.env appears to load values but Python cannot see them because the variables were not exported in the parent shell. It does not automatically read ./.env; pass project-local env files with --env-file .env so the destination for authenticated requests is explicit. CLI flags override exported environment variables, which override env-file values.
Source title format: --text-file "Title with spaces=/abs/path/to/file.md" uses the text before = as the Ghostwriter source title. If you omit =, the filename is used.
Use the bundled script whenever possible:
python3 skills/ghostwriter-one-off-podcast/scripts/create_one_off_podcast.py \
--env-file ~/.env \
--title "Project planning brief" \
--brief "Explain the source documents, tradeoffs, and next actions." \
--voice-preset openai-balanced \
--text-file "Project Notes=/path/to/project-notes.txt" \
--url "https://example.com/article" \
--save-response /path/to/project-planning-brief-response.json \
--download \
--output /path/to/project-planning-brief.mp3
Obsidian preview workflow:
python3 skills/ghostwriter-one-off-podcast/scripts/create_one_off_podcast.py \
--title "Project notes podcast" \
--brief "Explain these notes as a concise conversational briefing." \
--obsidian-note "/path/to/Vault/Project.md" \
--obsidian-folder "/path/to/Vault/Projects/Project" \
--obsidian-include "*.md" \
--obsidian-exclude "Archive/*" \
--obsidian-tag research \
--include-linked-notes \
--linked-note-depth 2 \
--include-backlinks \
--source-manifest /tmp/project-podcast-manifest.json \
--preview
Write a full submit-ready preview file only when needed:
python3 skills/ghostwriter-one-off-podcast/scripts/create_one_off_podcast.py \
--title "Project notes podcast" \
--brief "Explain these notes as a concise conversational briefing." \
--obsidian-note "/path/to/Vault/Project.md" \
--preview \
--preview-output /tmp/project-podcast-preview.json
Submit from a full preview file:
python3 skills/ghostwriter-one-off-podcast/scripts/create_one_off_podcast.py \
--env-file ~/.env \
--source-json /tmp/project-podcast-preview.json \
--research-briefing \
--voice-preset elevenlabs-research \
--save-response /path/to/project-notes-response.json \
--download \
--output /path/to/project-notes.mp3
Environment overrides:
export GHOSTWRITER_BASE_URL="https://ghostwriter.example.com"
export GHOSTWRITER_TOKEN="gw_..."
If using a .env file, pass it directly instead of relying on source ~/.env; sourced shell variables only reach Python if they are exported:
python3 skills/ghostwriter-one-off-podcast/scripts/create_one_off_podcast.py \
--env-file ~/.env \
--text-file "Note=/path/to/note.md" \
--download
The script prints JSON. On creation it includes episode_id, digest_ids, and status. With --poll, it prints the final episode detail. With --download or --output, it polls until ready, saves the MP3, and adds downloaded_path to the final JSON. Use --save-response PATH to write the final response JSON, including the transcript when returned, to a private file. --save-transcript PATH is accepted as an alias for agents that think in transcript-retention terms. Use --quiet for automation that should print only one concise status line.
Preview stdout includes the payload shape with text source content redacted plus _preview metadata with source counts, word counts, estimated source minutes, estimated TTS characters, warnings, source origins, tags, and content hashes. --preview-output writes the full submit-ready JSON to a private file and that file can be passed back with --source-json. --source-manifest PATH writes only the redaction-safe _preview metadata to a private JSON file for audit trails.
Generation override flags:
--voice-preset: one of openai-balanced, openai-energetic, openai-solo-analysis, elevenlabs-research, elevenlabs-formal, or elevenlabs-v3-dialogue.
--tts-provider: openai or elevenlabs.
--host-count: 1 for solo narration or 2 for a host dialogue.
--host-a-voice / --host-b-voice: provider voice name/ID. For ElevenLabs use voice IDs.
--expressiveness: ElevenLabs delivery mode — creative, natural (default), or robust. See "Model & Expressiveness Selection" below.
--style: casual, formal, or deep-dive.
--preferred-length-minutes, --script-model, and --script-timeout-seconds: per-episode generation preferences.
--research-briefing: appends guidance for AI-agent research notes, emphasizing evidence, uncertainty, contradictions, decisions, and next actions.
Exit codes:
0: episode ready, and download succeeded if requested
2: local configuration or input error
3: Ghostwriter API/network error
4: polling timed out
5: episode reached failed
6: audio download failed
Payload Shape
When calling the API directly, send:
{
"title": "Episode title",
"brief": "What the episode should focus on.",
"sources": [
{
"type": "text",
"title": "Document title",
"content": "Extracted document text..."
},
{
"type": "url",
"title": "Optional article title",
"url": "https://example.com/article"
}
]
}
Optional per-episode generation overrides are accepted under generation:
{
"generation": {
"tts_provider": "elevenlabs",
"elevenlabs_model_id": "eleven_v3",
"elevenlabs_expressiveness": "creative",
"host_count": 2,
"host_a_voice": "iP95p4xoKVk53GoZ742B",
"host_b_voice": "XrExE9yKIg1WjnnlVkGX",
"style": "deep-dive",
"preferred_length_minutes": 12
}
}
Model & Expressiveness Selection
Pick the ElevenLabs model and expressiveness per task; both are per-episode overrides that do not change saved global preferences.
Model (elevenlabs_model_id / --elevenlabs-model-id):
eleven_v3: most expressive. Unlocks inline audio tags ([laughs], [excited], [pauses], ...) in the script, dialogue-mode synthesis for 2-host episodes (shared scene context and natural turn-taking), and native text normalization. Alpha model — text-to-dialogue access depends on the account plan; Ghostwriter automatically falls back to per-segment synthesis when the endpoint is unavailable.
eleven_turbo_v2_5 (default): fast and roughly half the per-character cost of v3/multilingual. No audio tags — Ghostwriter strips bracketed tags before synthesis. Good for dry factual briefings.
eleven_multilingual_v2: highest consistency for non-English or mixed-language sources, v3-level cost, no audio tags.
Expressiveness (elevenlabs_expressiveness / --expressiveness):
creative: most emotional and responsive to audio tags; scripts get denser delivery cues. Best for narrative explainers and conversational episodes.
natural (default): balanced; closest to the neutral baseline of each voice.
robust: most stable and consistent, least responsive to tags — like the old controlled delivery. Best for formal or information-dense briefings.
Suggested combinations: eleven_v3 + creative for storytelling or banter-heavy dialogue; eleven_v3 + natural for research briefings that should still feel alive; eleven_turbo_v2_5 + robust for fast, low-cost, no-frills news reads. The elevenlabs-v3-dialogue preset applies eleven_v3 + creative with the Eric/Jessica pairing (see the voice inventory below).
Voice Catalog
Ghostwriter exposes the shared catalog at /api/podcast/voices. Use these IDs in one-off generation overrides or helper flags.
| Provider | Name | ID | Vibe | Best suited for |
|---|
| OpenAI | Alloy | alloy | Balanced, neutral, clear | General research briefings and mixed-source summaries |
| OpenAI | Echo | echo | Warm, conversational, steady | Context-setting, explanatory turns, and recaps |
| OpenAI | Fable | fable | Expressive, story-forward, lighter | Narrative explainers and approachable solo summaries |
| OpenAI | Onyx | onyx | Deep, calm, authoritative | Serious analysis, longer-form synthesis, and solo narration |
| OpenAI | Nova | nova | Bright, quick, energetic | Hooks, momentum, and lively research updates |
| OpenAI | Shimmer | shimmer | Polished, friendly, precise | Executive-style briefings and concise summaries |
| ElevenLabs | Chris | iP95p4xoKVk53GoZ742B | Natural, confident, presenter-like | Host A in research briefings and decision summaries |
| ElevenLabs | Matilda | XrExE9yKIg1WjnnlVkGX | Warm, clear, explanatory | Host B context, definitions, and clarifying questions |
| ElevenLabs | George | JBFqnCBsd6RMkjVDRZzb | Measured, grounded, direct | Formal analysis and slower-paced synthesis |
| ElevenLabs | Bella | hpp4J3VqNfWAUOO0d1Us | Friendly, responsive, conversational | Accessible companion host and lighter recap segments |
Useful pairings:
Alloy + Echo: balanced OpenAI research briefing.
Nova + Fable: more energetic narrative episode.
Chris + Matilda: default ElevenLabs research briefing.
George + Bella: formal analysis with warmer companion context.
Note: Bella (hpp4J3VqNfWAUOO0d1Us) is not part of the current ElevenLabs premade set — it must exist in the account's My Voices. All other ElevenLabs IDs in the curated catalog are premade voices from the inventory below.
ElevenLabs Premade Voice Inventory (v3-ready)
Every ElevenLabs account created before ~March 2026 includes these 20 premade voices. Voice IDs are model-independent — the same ID works with eleven_turbo_v2_5, eleven_multilingual_v2, and eleven_v3; only model_id changes. ElevenLabs' v3 guidance: voice choice is the single most important v3 parameter — expressive/conversational voices respond best to audio tags, while neutral news/narration voices are the most stable baseline.
| Name | Voice ID | Gender | Accent | Official label | Podcast fit |
|---|
| Aria | 9BWtsMINqrJLrRacOk9x | Female | American | expressive, social media | Expressive — strong audio-tag response on v3 |
| Roger | CwhRBWXzGAHq8TQ4Fs17 | Male | American | confident, social media | Confident co-host |
| Sarah | EXAVITQu4vr4xnSDxMaL | Female | American | soft, news | Stable neutral baseline |
| Laura | FGY2WhTYpPnrIDTdsKH5 | Female | American | upbeat, social media | Energetic host |
| Charlie | IKne3meq5aSn9XLyUdCD | Male | Australian | natural, conversational | Natural dialogue host |
| George | JBFqnCBsd6RMkjVDRZzb | Male | British | warm, narration | Warm narrator/anchor |
| Callum | N2lVS1w4EtoT3dr4eOWO | Male | Transatlantic | intense, characters | Character voice; avoid for neutral hosting |
| River | SAz9YHcvj6GT2YYXdXww | Non-binary | American | confident, social media | Confident host |
| Liam | TX3LPaxmHKxFdv7VOQHJ | Male | American | articulate, narration | Clear young narrator |
| Charlotte | XB0fDUnXU5powFXDhCwa | Female | Swedish | seductive, characters | Character voice |
| Alice | Xb7hH8MSUJpSbSDYk0k2 | Female | British | confident, news | Stable news/anchor tone |
| Matilda | XrExE9yKIg1WjnnlVkGX | Female | American | friendly, narration | Friendly explainer host |
| Will | bIHbv24MWmeRgasZH58o | Male | American | friendly, social media | Casual young host |
| Jessica | cgSgspJ2msm6clMCkdW9 | Female | American | expressive, conversational | Strong v3 dialogue pick |
| Eric | cjVigY5qzO86Huf0OWal | Male | American | friendly, conversational | Strong podcast pick |
| Chris | iP95p4xoKVk53GoZ742B | Male | American | casual, conversational | Casual conversational host |
| Brian | nPczCjzI2devNBz1zQrb | Male | American | deep, narration | Deep, very stable narrator |
| Daniel | onwK4e9ZLuTAKqWW03F9 | Male | British | authoritative, news | News-anchor delivery |
| Lily | pFZP5JQG7iQjIQuC4Bku | Female | British | warm, narration | Warm narrator |
| Bill | pqHfZKP75CvOlQylNhV4 | Male | American | trustworthy, narration | Older, trustworthy narrator |
Additional v3-focused pairings from this inventory:
Jessica + Eric: expressive + friendly conversational — best premade pairing for eleven_v3 + creative dialogue episodes.
Charlie + Laura: natural + upbeat for lively casual episodes.
Daniel + Alice: authoritative + confident British news tone for formal briefings (robust).
Brian + Lily: deep + warm narration for measured long-form synthesis.
Caveats:
- Premade voices expire 2026-12-31. ElevenLabs is replacing Default voices; legacy voice IDs will auto-route to replacements via the API. Before hardcoding IDs in automation, check the account's current voices and the replacement table in the "What are Default voices?" help article (help.elevenlabs.io).
- ElevenLabs also curates a "Best voices for Eleven v3" collection in the voice library (e.g. Hope, Jane, James, Mark - Natural Conversations) — these often outperform premade voices on v3 but must be added to the account's My Voices before the API can use them.
- Professional voice clones are not fully optimized for v3; prefer premade or designed voices for v3 episodes.
Operating Rules
- Keep documents in user-provided order unless the user asks for a different structure.
- Prefer extracted text for private/local documents; Ghostwriter cannot fetch files from the user's machine.
- For Obsidian, prefer
--obsidian-note and --obsidian-folder; the helper strips YAML frontmatter, comments, embeds, wikilinks, block IDs, tags, and common Markdown link noise before submitting.
- Use
--include-linked-notes only when the user wants wikilink context included. Keep --linked-note-depth bounded and review preview warnings for unresolved links.
- For PDFs, DOCX, or web clippings, reduce each source to clean text before submitting.
- Do not include secrets, API tokens, or private credentials inside source content.
- For wiki or research use cases, persist the Ghostwriter response with
--save-response so the generated synthesis remains auditable separately from verbatim source material.
- If a document is too large, the helper splits it into chunks with titles such as
Roadmap - Part 1.
- If preview produces more than 20 sources after splitting and linked-note expansion, narrow the selection before submitting.
- If the episode remains in
generating_script or generating_audio, continue polling at a reasonable interval instead of retrying immediately.
- If the episode fails, fetch the episode detail and report the exact
error_message.