
sag
CLIA macOS say-style text-to-speech CLI that uses ElevenLabs or 60db to speak text aloud or save it as audio (ElevenLabs supports streaming playback), and can list and switch voices. Needs an API key for the chosen service.
sag 🗣️ — “Mac-style speech with ElevenLabs or 60db”
One-liner TTS that works like say: stream to speakers by default, list voices, or save audio files.
Install
Homebrew (macOS):
brew install steipete/tap/sag # auto-taps steipete/tap
Prebuilt binaries:
- Download Linux, macOS, and Windows archives from the latest GitHub release.
- macOS downloads require macOS 15 or later and are Developer ID signed and notarized by Peter Steinberger, so direct downloads pass Gatekeeper.
- On Linux, unpack the
linux_amd64archive and placesagsomewhere on yourPATH, for example/usr/local/bin.
Go toolchain:
go install github.com/steipete/sag/cmd/sag@latest
Requires Go 1.25+.
Debian/Ubuntu source build prerequisites:
sudo apt install build-essential pkg-config libasound2-dev
Configuration
sag supports two TTS providers and auto-selects one from your configured credentials:
- ElevenLabs —
ELEVENLABS_API_KEY(or--api-key, or--api-key-file/ELEVENLABS_API_KEY_FILE/SAG_API_KEY_FILE) - 60db (
api.60db.ai) —SIXTYDB_API_KEY(orSIXTYDB_API_KEY_FILE)
Selection rules:
- Only one key set → that provider is used.
- Both keys set → error; unset one provider key and retry.
- Neither set → error.
Optional ElevenLabs defaults: ELEVENLABS_VOICE_ID or SAG_VOICE_ID. Override the active provider host with --base-url.
60db support is intentionally narrow and follows the live API behavior:
- voice discovery merges
GET /default-voicesandGET /myvoices - synthesis uses
POST /tts-synthesize, validates every NDJSON frame, and wraps the returned 48 kHz PCM as WAV - incomplete or malformed provider responses fail instead of producing a truncated file
- 60db currently supports WAV playback/output only; its live synthesis response is buffered rather than streamed
Shared speak flags that work on both providers include --voice, --speed / --rate, --stability, --similarity, --play, --output, --timeout, and --metrics. On 60db, --format accepts only wav, and an explicit --stream is rejected.
ElevenLabs-only speak flags fail fast on 60db: --model-id, --style, --speaker-boost / --no-speaker-boost, --seed, --normalize, --lang, and --latency-tier. --stability / --similarity still use the CLI's 0..1 range and are scaled to 60db's documented 0..100 API values. See docs/providers.md for provider-specific details.
Usage
Features:
- macOS
say-style default:sag "Hello"routes tospeakautomatically. - Streaming playback to speakers with optional file output.
- Voice discovery via
sag voicesand-v ?. - Speed/rate controls, latency tiers, and format inference from output extension.
- Model selection via
--model-id(defaults toeleven_v3; useeleven_multilingual_v2for a stable baseline).
Speak with ElevenLabs streaming:
sag speak -v Roger "Hello world"
Call it like macOS say: omitting the subcommand pipes text to speak by default.
sag "Hello world"
macOS say compatibility shortcuts (subcommand optional):
sag -v Roger -r 200 "Faster speech"
sag -o out.mp3 "Save to file"
sag -v ? # list voices
More examples:
echo "piped input" | sag speak -v Roger
sag speak -v Roger --stream --latency-tier 3 "Faster start"
sag speak -v Roger --speed 1.2 "Talk a bit faster"
sag speak -v Roger --model-id eleven_multilingual_v2 "Use stable v2 baseline"
sag speak -v Roger --output out.wav --format pcm_44100 "Wave output"
SIXTYDB_API_KEY=... sag speak -v Aria --output out.wav --no-play "60db WAV output"
Key flags (subset):
-v, --voicevoice name or ID (?to list)--api-key-fileread API key from a file-r, --ratewords per minute (maps to ElevenLabs speed; default 175)-f, --input-fileread text from file (-for stdin)-o, --outputwrite audio file; format inferred by extension (.wav-> PCM,.mp3-> MP3)--speedexplicit speed multiplier (0.5–2.0)--stabilityv3:0|0.5|1(Creative/Natural/Robust); v2/v2.5: 0..1 (higher = more consistent, less expressive)--similarity/--similarity-boost0..1 (higher = closer to the reference voice)--style0..1 (higher = more stylized delivery; model/voice dependent)--speaker-boost/--no-speaker-boosttoggle clarity boost (model dependent)--seed0..4294967295 best-effort repeatability across runs--normalizeauto|on|offnumbers/units/URLs normalization (when set)--langen|de|fr|...2-letter ISO 639-1 language code (when set)--stream/--no-streamstream while generating (default on)--latency-tier0–4 lower latency tiers--play/--no-playcontrol speaker playback--player auto|afplay|otochoose playback backend (autousesafplayon macOS andotoelsewhere; also configurable viaSAG_PLAYER)--timeoutmaximum TTS generation time, e.g.--timeout 5m;0means no internal timeout (default, also configurable viaSAG_TIMEOUT)--metricsprint basic stats to stderr
Voices:
sag voices --search english --limit 20
sag voices --search english --limit 5 --try
sag voices --query "crazy scientist" --limit 5 --try
sag voices --label accent=british --label use_case=character --limit 10
Prompting (make it sound better)
Run:
sag prompting
Highlights:
- v2/v2.5: SSML pauses via
<break time="1.5s" />(v3 does not support SSML breaks). - v3: use audio tags like
[whispers]and pause tags like[short pause]. - Use the voice knobs:
--stability,--similarity,--style,--speaker-boost, plus request controls--seed,--normalize,--lang.
Models / engines
sag supports any ElevenLabs model_id via --model-id (we pass it through). Practical defaults + common IDs:
| Engine | --model-id | Prompting style | Best for |
|---|---|---|---|
| v3 (alpha) | eleven_v3 (default) | Audio tags like [whispers], [short pause] (no SSML <break>) | Most expressive / “acting” |
| v2 (stable) | eleven_multilingual_v2 | SSML <break> supported | Reliable baseline, simple prompts |
| v2.5 Flash | eleven_flash_v2_5 | SSML <break> supported | Ultra-low latency (~75ms) + 50% lower price per character |
| v2.5 Turbo | eleven_turbo_v2_5 | SSML <break> supported | Low latency (~250–300ms) + 50% lower price per character |
Notes:
- SSML
<break>works on v2/v2.5, not v3. Use pause tags on v3 instead. - Input limits differ by engine (v3: 5,000 chars; v2: 10,000 chars; v2.5 Turbo/Flash: 40,000 chars). If you hit limits, chunk text and stitch audio.
--normalize onmay not be available for v2.5 Turbo/Flash (higher latency); preferauto/offif it errors.- Source of truth: ElevenLabs “Models” docs.
Timeout considerations
Longer text can take more than a minute to generate, especially with eleven_v3. By default, sag does not add its own generation timeout, so callers such as agents, scripts, shells, or CI jobs can choose the right outer timeout without getting a valid-but-truncated audio file from an internal SIGTERM.
If you want sag itself to stop a request, set an explicit duration:
sag --timeout 5m --no-play -o long.mp3 "Long text..."
SAG_TIMEOUT=5m sag --no-play -o long.mp3 "Long text..."
For long agent/script runs, give the outer process timeout enough headroom and verify generated audio duration when truncation would matter:
ffprobe -v quiet -show_entries format=duration -of csv=p=0 long.mp3
Development
- With pnpm:
pnpm formatpnpm lintpnpm testpnpm buildpnpm sag -- --help(passes args to the Go binary)
- Direct Go:
- Format:
go fmt ./... - Lint:
golangci-lint run - Tests:
go test ./... - Build:
go build ./cmd/sag
- Format:
Limitations
- One provider API key is required.
- Voice defaults to first available if not provided.
- Non-mac platforms: playback still works via MP3/WAV decoding +
oto, but device selection flags are no-ops.
Ressources associées

video-use
SkillBrowser Use's video editing skill: put raw footage in a folder and chat with Claude Code or Codex to get a finished cut, with filler words and pauses removed, color grading, burned-in subtitles, animations and per-cut self-checks. Needs ffmpeg, Python (uv) and an ElevenLabs API key.
browser-use/video-use28.2k
edge-tts
CLIConverts text to speech with Microsoft Edge's online TTS service and can generate subtitle files at the same time. No API key needed, with voices for many languages. Not an official Microsoft project and depends on that online service staying available.
rany2/edge-tts12.3k
Summarize
CLIOne command extracts or summarizes web pages, PDFs, YouTube videos, podcasts and media files, with JSON output. Summaries can use many model APIs, local models or a logged-in Codex or Claude CLI; plain extraction needs no key. Also has a browser sidebar extension.
steipete/summarize6.7k
Podcastfy
CLIOpen-source alternative to NotebookLM's podcast feature: turns web pages, PDFs, images, YouTube videos or text into two-host conversational audio, useful for lecture notes or articles. Uses Gemini and OpenAI API keys by default, or local models plus free Edge TTS.
souzatharsis/podcastfy6.6k
Piper
CLILocal neural text-to-speech engine that converts text to speech offline, with voices in dozens of languages, handy for listening materials or narrating articles and course slides. Needs Python; download a voice model before first use. The project is currently looking for maintainers.
OHF-Voice/piper1-gpl5.9k
Kokoro TTS
CLILocal text-to-speech CLI built on the open Kokoro model: batch-converts TXT, EPUB and PDF to MP3 or WAV by chapter, with multiple languages, voice blending and speed control; handy for audio versions of course materials. Needs Python 3.11-3.12 and two model files downloaded first.
nazdridoy/kokoro-tts1.9k