Speak Summary
GitHubOfficialFirst committed by Sam-RoweDistributed by GitHub
- Skill
- Content generator
- Automation and integrations
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
Speak Summary transforms written text, markdown, or summaries into listenable MP3 audio files. Designed as a terminal step in an automation chain, it processes text by first rewriting it for the ear to ensure natural flow and clarity, then synthesising it using local CPU-based neural text-to-speech. It is built to operate entirely offline, making it suitable for confidential content or headless environments.
What it does
- Rewrites written prose into a script optimized for spoken delivery
- Synthesizes text into mono MP3 audio files using local neural models
- Supports voice cloning from provided audio samples
- Handles text-to-speech processing entirely on the local CPU
- Provides guidance on structuring content for audio consumption
- Integrates with other skills to create automated audio briefings
When to use it
- When you need to convert meeting minutes or daily summaries into audio
- When you want to listen to a report or briefing during a commute
- When you require an offline solution for generating spoken content
- When you need a companion audio version of a written document
Advantages
- Operates fully offline without external cloud speech services
- Ensures privacy by keeping all data processing local
- Works in headless environments or CI containers
- Provides a structured approach to adapting text for the ear
Usage examples
- “Turn this meeting summary into a podcast version for my commute.”
- The agent rewrites the summary into a spoken script and generates an MP3 file ready for playback.
- “I want to listen to this report, make an MP3.”
- The agent processes the text, optimizes it for audio, and saves the resulting file to the designated directory.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/github/speak-summary/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip speak-summary.zip -d .agents/skills
Documentation by Sam-Rowe
Speak Summary
Turn written text into audio someone will actually want to listen to.
This skill is deliberately a terminal step in a chain. Another skill (or you)
produces the text; this one makes it listenable. It pairs naturally with
roundup, daily-prep, meeting-minutes, or any summarisation work.
Everything runs locally on CPU. No text is sent to a cloud speech service, which matters when the content is confidential, and it means the skill works in a headless cloud agent or CI container just as well as on a laptop.
Prerequisites
The synthesis engine is Kyutai pocket-tts,
a small neural TTS model designed to run on CPUs.
The bundled script installs it automatically into a cached virtualenv on first use, so usually you need do nothing. To install it explicitly:
pip install pocket-tts # any platform
brew install pocket-tts # macOS, if preferredpocket-tts requires Python >=3.10 and <3.15. The script searches for a
compatible interpreter rather than assuming python3 is one — worth knowing if
you are on a very new Python, where installation would otherwise fail.
You also need an encoder. ffmpeg is strongly preferred (brew install ffmpeg
or apt-get install -y ffmpeg); on macOS the script falls back to the built-in
afconvert and emits .m4a instead of .mp3.
The first run downloads the model (~1GB) from Hugging Face. After that it is fully offline and synthesises roughly 6x faster than real-time.
The important step: rewrite for the ear
Do not feed written text straight into the synthesiser. Prose that reads well on screen is tiring to listen to. Rewriting it first is what separates a useful audio digest from an unlistenable one.
Produce a spoken script that:
Opens with orientation. What this is, what it covers, roughly how long it runs.
Replaces bullets with connective prose. "First… The bigger one is… Finally…" — a listener has no visual structure to lean on, so carry it in the language.
Expands abbreviations on first use. "PR" becomes "pull request", "CI" becomes "continuous integration". Acronyms that read fine are noise when spoken.
Speaks dates and numbers naturally. "the twentieth of August", not "2026-08-20". "About three thousand", not "2,847".
Never reads URLs aloud. Say "linked in the written version" instead.
Uses short sentences. Split anything past roughly 25 words.
Signposts transitions. "Turning to the product side…", "Two things need your attention…".
Ends with the actions. Recap what the listener should do, since that is what they need to retain and they cannot scroll back.
Drops anything purely visual. Tables, code blocks, and diagrams should be summarised in a sentence or omitted, never read out.
Write this spoken script to its own .txt file. Keep the original written
version with its links intact — the audio is a companion to it, not a
replacement. The user will want to click through later.
Synthesise
./scripts/tts.sh <input.txt> <output.mp3> [voice.safetensors]The script strips any residual markdown, splits the text on sentence boundaries into ~600 character chunks (quality degrades on long single inputs), synthesises each chunk, and concatenates the result into a mono MP3 at 96kbps — small enough to sync to a phone, good enough for speech.
Environment overrides:
| Variable | Purpose |
|---|---|
SPEAK_TTS_BIN | Path to a specific pocket-tts binary; skips all auto-detection. |
SPEAK_TTS_HOME | Where to create/find the cached virtualenv. Default ~/.cache/speak-summary/venv. |
Voices
The default English voice is alba. To use a different one, pocket-tts
supports voice cloning from a short clean audio sample:
pocket-tts export-voice --helpPass the resulting .safetensors file as the third argument to the script.
Only clone a voice you have the rights to use. Do not clone a real person's voice — colleague, customer, or public figure — without their explicit consent.
Output
Default to
~/Music/Briefings/unless the user says otherwise; it is easy to point a phone or podcast app at.Name files
<subject>-<YYYY-MM-DD>.mp3.Report the path, duration, and size.
Offer to play it:
afplay <path>on macOS,ffplay -nodisp -autoexit <path>elsewhere.
Length guidance
Aim for 4–6 minutes for a routine digest, which is roughly 600–900 spoken words at a natural pace. If the source would run past about 10 minutes, say so and offer either a tighter edit or a split into multiple files — attention drops off sharply beyond that for informational audio.
Chaining onto other skills
The natural pattern is gather → summarise → speak:
roundup→speak-summary— a spoken version of the status briefing.daily-prep→speak-summary— tomorrow's schedule, listened to tonight.meeting-minutes→speak-summary— catch up on a meeting you missed.
When invoked as part of a chain, do not re-summarise. The upstream skill owns what to say; this skill owns how it sounds. Take its output, rewrite it for the ear, and synthesise.
To run unattended (a briefing waiting before breakfast), schedule the upstream skill with a workflow and have it finish by calling this one.
Troubleshooting
Audio cuts off mid-sentence. A chunk exceeded the model's comfortable length. Shorten the sentences in the spoken script.
Words mispronounced. Spell them phonetically in the input — "Kubernetes" as "koo-ber-net-eez". This is a normal part of preparing a spoken script.
First run is slow. That is the one-off model download. Later runs start in about a second.
pocket-tts not found after install. The virtualenv may be stale, or your
python3 may be outside the supported 3.10–3.14 range. Delete
~/.cache/speak-summary/venv and re-run, or point SPEAK_TTS_BIN at a known binary.
Files
- ATTRIBUTION.mdAdded by AutoGineer859 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md6.3 KiB
- scripts/tts.shScript5.8 KiB
scripts/tts.sh
#!/usr/bin/env bash
# Convert a plain-text briefing into an MP3 using local neural TTS (Kyutai pocket-tts).
#
# Usage: tts.sh <input.txt> <output.mp3> [voice.safetensors]
#
# CPU-only and fully offline after the first model download, so it runs the same
# on an Apple Silicon Mac and in a Linux cloud agent container. No text is sent
# to any cloud TTS service.
#
# Resolution order for the engine:
# 1. pocket-tts already on PATH (e.g. `brew install pocket-tts`)
# 2. a cached venv at $SPEAK_TTS_HOME (default ~/.cache/speak-summary/venv)
# 3. create that venv and `pip install pocket-tts`
# Set SPEAK_TTS_BIN to point at a specific pocket-tts binary to skip all this.
set -euo pipefail
IN="${1:?usage: tts.sh <input.txt> <output.mp3> [voice.safetensors]}"
OUT="${2:?usage: tts.sh <input.txt> <output.mp3> [voice.safetensors]}"
VOICE="${3:-}"
TTS_HOME="${SPEAK_TTS_HOME:-$HOME/.cache/speak-summary/venv}"
# pocket-tts supports Python >=3.10,<3.15. The system python3 is often outside
# that range, so search for a usable interpreter rather than assuming.
find_python() {
for c in python3.14 python3.13 python3.12 python3.11 python3.10 python3; do
p="$(command -v "$c" 2>/dev/null)" || continue
"$p" -c 'import sys; raise SystemExit(0 if (3,10) <= sys.version_info < (3,15) else 1)' 2>/dev/null \
&& { echo "$p"; return 0; }
done
return 1
}
resolve_tts() {
if [ -n "${SPEAK_TTS_BIN:-}" ]; then echo "$SPEAK_TTS_BIN"; return; fi
if command -v pocket-tts >/dev/null 2>&1; then command -v pocket-tts; return; fi
if [ -x "$TTS_HOME/bin/pocket-tts" ]; then echo "$TTS_HOME/bin/pocket-tts"; return; fi
PY="$(find_python)" || {
cat >&2 <<'MSG'
No suitable Python found. pocket-tts requires Python >=3.10 and <3.15.
Install one (e.g. 'brew install [email protected]' or 'apt-get install -y python3.12-venv'),
or install pocket-tts yourself and point SPEAK_TTS_BIN at the binary.
MSG
exit 1
}
echo "pocket-tts not found; creating a virtualenv at $TTS_HOME using $PY (one-off, a few minutes)..." >&2
"$PY" -m venv "$TTS_HOME" >&2 || { echo "Failed to create virtualenv (is the venv module installed?)." >&2; exit 1; }
"$TTS_HOME/bin/pip" install --quiet --upgrade pip >&2
"$TTS_HOME/bin/pip" install --quiet pocket-tts >&2 || { echo "Failed to install pocket-tts." >&2; exit 1; }
[ -x "$TTS_HOME/bin/pocket-tts" ] || { echo "pocket-tts install completed but the binary is missing." >&2; exit 1; }
echo "$TTS_HOME/bin/pocket-tts"
}
TTS="$(resolve_tts)"
[ -x "$TTS" ] || { echo "TTS engine not executable: $TTS" >&2; exit 1; }
# Encoder: prefer ffmpeg; fall back to macOS afconvert (AAC in .m4a) if absent.
ENCODER=""
if command -v ffmpeg >/dev/null 2>&1; then ENCODER="ffmpeg"
elif command -v afconvert >/dev/null 2>&1; then ENCODER="afconvert"
else
echo "Need ffmpeg to encode audio. Install with 'brew install ffmpeg' or 'apt-get install -y ffmpeg'." >&2
exit 1
fi
WORK="$(mktemp -d)"
trap 'rm -rf "$WORK"' EXIT
# pocket-tts quality degrades on very long inputs, so split into ~600-char chunks
# on sentence boundaries, synthesise each, then concatenate.
python3 - "$IN" "$WORK" <<'PY'
import re, sys, pathlib
src = pathlib.Path(sys.argv[1]).read_text()
work = pathlib.Path(sys.argv[2])
# Strip markdown that would otherwise be read aloud as noise.
src = re.sub(r'```.*?```', ' ', src, flags=re.S) # fenced code
src = re.sub(r'\[([^\]]+)\]\([^)]+\)', r'\1', src) # links -> label
src = re.sub(r'^\s*#{1,6}\s*', '', src, flags=re.M) # headings
src = re.sub(r'^\s*[-*+]\s+', '', src, flags=re.M) # bullets
src = re.sub(r'[*_`>|~]+', '', src) # emphasis/code/table pipes
src = re.sub(r'https?://\S+', '', src) # bare URLs
src = re.sub(r'[ \t]+', ' ', src)
src = re.sub(r'\n{2,}', '\n\n', src).strip()
MAX = 600
chunks, cur = [], ""
for sent in re.split(r'(?<=[.!?])\s+|\n\n', src):
sent = sent.strip()
if not sent:
continue
# A single sentence longer than MAX is split on commas as a last resort.
while len(sent) > MAX:
cut = sent.rfind(',', 0, MAX)
cut = cut if cut > MAX // 2 else sent.rfind(' ', 0, MAX)
cut = cut if cut > 0 else MAX
if cur:
chunks.append(cur); cur = ""
chunks.append(sent[:cut].strip())
sent = sent[cut:].strip(' ,')
if len(cur) + len(sent) + 1 > MAX:
if cur:
chunks.append(cur)
cur = sent
else:
cur = f"{cur} {sent}".strip()
if cur:
chunks.append(cur)
if not chunks:
raise SystemExit("No speakable text found in input.")
for i, c in enumerate(chunks):
(work / f"chunk_{i:04d}.txt").write_text(c)
PY
N=0
for f in "$WORK"/chunk_*.txt; do
IDX="$(basename "$f" .txt)"
ARGS=(generate --quiet --text "$(cat "$f")" --output-path "$WORK/$IDX.wav")
[ -n "$VOICE" ] && ARGS+=(--voice "$VOICE")
N=$((N+1))
echo " synthesising chunk $N ..." >&2
"$TTS" "${ARGS[@]}" >/dev/null
echo "file '$WORK/$IDX.wav'" >> "$WORK/list.txt"
done
mkdir -p "$(dirname "$OUT")"
if [ "$ENCODER" = "ffmpeg" ]; then
ffmpeg -hide_banner -loglevel error -y -f concat -safe 0 -i "$WORK/list.txt" \
-c:a libmp3lame -b:a 96k -ar 24000 -ac 1 "$OUT"
else
# afconvert cannot concat, so join the WAVs first, then encode to AAC.
python3 - "$WORK" "$WORK/joined.wav" <<'PY'
import sys, wave, pathlib
work, out = pathlib.Path(sys.argv[1]), sys.argv[2]
parts = sorted(work.glob("chunk_*.wav"))
with wave.open(parts[0], 'rb') as w0:
params = w0.getparams()
with wave.open(out, 'wb') as o:
o.setparams(params)
for p in parts:
with wave.open(str(p), 'rb') as w:
o.writeframes(w.readframes(w.getnframes()))
PY
OUT="${OUT%.mp3}.m4a"
afconvert -f m4af -d aac -b 96000 "$WORK/joined.wav" "$OUT"
echo "Note: ffmpeg not available; wrote AAC (.m4a) instead of MP3." >&2
fi
SIZE="$(du -h "$OUT" | cut -f1)"
echo "Wrote $OUT ($SIZE, $N chunks)"
Requirements and compatibility
Written for: GitHub Copilot
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs a command-line tool, Needs Python
Security
What our scanner noticed· Low risk
- Instructions that use the network
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- First committed by
Sam-Rowe
- Publisher
- GitHub (Official)
- Distributed by
- GitHub
- Source
- github/awesome-copilot · skills/speak-summary · commit 797337f
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Contributors
- Sam-Rowe
AutoGineer is not affiliated with or endorsed by Sam-Rowe or GitHub.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download speak-summary.zip