Speak text aloud from the terminal. Built for LLM agents to talk to you, with offline system TTS when no API key is configured.
Project description
saynow
Speak text aloud from the terminal. Built so LLM agents can talk to you.
saynow "the build finished, 42 tests passed"
Works with no configuration — it falls back to your OS's built-in voice, which is offline, free, and needs no API key. Add a key for better audio.
Install
pip install saynow # or: uv tool install saynow
No third-party dependencies — standard library only. An identical npm package exists for Node; both read the same config file, so pick whichever runtime you already have.
Usage
saynow "text to speak" # speak an argument
echo "text" | saynow # speak stdin
pytest 2>&1 | tail -1 | saynow # speak a command's last line
saynow -p openai -v nova "hello" # pick a provider and voice
saynow --save note.mp3 "hello" # write a file instead of playing
| Provider | Quality | Needs |
|---|---|---|
system |
Fair | Nothing — built into the OS |
openai |
Good | OPENAI_API_KEY |
elevenlabs |
Best | ELEVENLABS_API_KEY |
openrouter |
Good | OPENROUTER_API_KEY |
If a cloud provider is configured but its key is missing, saynow warns on
stderr and speaks with the system voice anyway. Use --strict to opt out.
Concurrent calls are serialized so speech never overlaps.
Configure
saynow init # interactive setup
saynow config set provider openai
saynow voices # available voices
saynow models # OpenRouter models that can speak
Config lives at ~/.config/saynow/config.json, mode 0600. Precedence is
defaults → config file → environment → flags. API keys are never accepted as
flags, since argv is visible to ps and lands in shell history.
Use it from an agent
Add this to your CLAUDE.md, AGENTS.md, or equivalent:
You can speak to the user out loud with `saynow "<text>"`. Use it for things
worth interrupting for: a long task finishing, a question that blocks
progress, or an error that needs attention. Keep it to one short sentence —
it is spoken aloud, not read. Do not narrate routine progress.
The bubble
The npm build also shows a floating bubble with the transcript and an optional
reply box (--ask). That is not yet ported here, so the pip build speaks and,
with --ask, exits 2. See the project README.
Full reference
saynow --help # complete reference: every flag, setting, and exit code
License
MIT — source at https://github.com/dhruvyad/saynow
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file saynow-0.8.0.tar.gz.
File metadata
- Download URL: saynow-0.8.0.tar.gz
- Upload date:
- Size: 21.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ff5a9b8e9be41dd63f0629d94e130c7f7bf15d11e97ec548941e99c2fda9b14e
|
|
| MD5 |
72a5a8457bae2148211b4b1fe4a6065b
|
|
| BLAKE2b-256 |
963d8572bde9fc7b81f1659e4503f5c043e184a6caebb349cbbc3ecad1d80ba3
|
Provenance
The following attestation bundles were made for saynow-0.8.0.tar.gz:
Publisher:
release.yml on dhruvyad/saynow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
saynow-0.8.0.tar.gz -
Subject digest:
ff5a9b8e9be41dd63f0629d94e130c7f7bf15d11e97ec548941e99c2fda9b14e - Sigstore transparency entry: 2257378678
- Sigstore integration time:
-
Permalink:
dhruvyad/saynow@b615faff4cd6977783bdf8704de7e92c380ee51f -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/dhruvyad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b615faff4cd6977783bdf8704de7e92c380ee51f -
Trigger Event:
release
-
Statement type:
File details
Details for the file saynow-0.8.0-py3-none-any.whl.
File metadata
- Download URL: saynow-0.8.0-py3-none-any.whl
- Upload date:
- Size: 24.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ed9483fa58a853cd5b2381f29794189664831fca0f7f66d64c3bdc40df80f73
|
|
| MD5 |
1aa1eb4b7e5766019c5940dff7335cb7
|
|
| BLAKE2b-256 |
e2809fd65b6ad6765acf8537a6c4cd706937cc42bed51c3f7b17a40dde78882b
|
Provenance
The following attestation bundles were made for saynow-0.8.0-py3-none-any.whl:
Publisher:
release.yml on dhruvyad/saynow
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
saynow-0.8.0-py3-none-any.whl -
Subject digest:
4ed9483fa58a853cd5b2381f29794189664831fca0f7f66d64c3bdc40df80f73 - Sigstore transparency entry: 2257378689
- Sigstore integration time:
-
Permalink:
dhruvyad/saynow@b615faff4cd6977783bdf8704de7e92c380ee51f -
Branch / Tag:
refs/tags/v0.8.0 - Owner: https://github.com/dhruvyad
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@b615faff4cd6977783bdf8704de7e92c380ee51f -
Trigger Event:
release
-
Statement type: