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 at all — it falls back to your operating system's built-in voice, which is offline, free, and needs no API key. Configure a cloud provider when you want it to sound good.
Status: not published to PyPI
The shipping build of saynow is the npm package. This Python port is a complete, working equivalent that lives in the repo but is deliberately unpublished — maintaining two implementations in lockstep is a cost worth paying only once someone actually needs the Python one.
The name is available on PyPI, so it can be published at any time. Open an issue if you want it.
Install from source
git clone https://github.com/dhruvyad/saynow
uv tool install ./saynow/python
No third-party dependencies — it uses only the standard library. Both builds read the same config file, so they are interchangeable on one machine.
Usage
saynow "text to speak" # speak an argument
echo "text" | saynow # speak stdin
pytest 2>&1 | tail -1 | saynow # speak the last line of a command
| Flag | Meaning |
|---|---|
-v, --voice <name> |
Voice to use — see saynow voices |
-p, --provider <id> |
system, openai, or elevenlabs |
-r, --rate <n> |
Words per minute (system provider) |
-s, --speed <n> |
Playback speed 0.25–4.0 (cloud providers) |
--save <file> |
Write audio to a file instead of playing it |
--no-queue |
Speak immediately, overlapping in-flight speech |
--strict |
Fail rather than falling back to the system voice |
-q, --quiet |
Suppress warnings on stderr |
Configuration
saynow init # interactive setup
saynow config list # show settings, API keys redacted
saynow config set provider openai
Config is stored at ~/.config/saynow/config.json with 0600 permissions.
Precedence is defaults → config file → environment → flags.
API keys are never accepted as command-line flags — argv is visible in shell
history and to ps.
Providers
| Provider | Quality | Cost | Needs |
|---|---|---|---|
system |
Fair | Free | Nothing — built into the OS |
openai |
Good | Paid | OPENAI_API_KEY |
elevenlabs |
Best | Paid | ELEVENLABS_API_KEY |
If a cloud provider is selected but no key is available, saynow warns on stderr
and speaks with the system voice anyway. Pass --strict to opt out.
Using 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.
Concurrent invocations are serialized with a lock file, so three calls in a row produce three sentences rather than one muddle.
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.1.0.tar.gz.
File metadata
- Download URL: saynow-0.1.0.tar.gz
- Upload date:
- Size: 9.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a15212e284eca70d95d0da65114cffa27c8b58b099d28b5436701cd562c5076
|
|
| MD5 |
11b5c44dc9042614b1ac6bb2e5eb0118
|
|
| BLAKE2b-256 |
84cdaf953dbce136eb512856de4994ddee91a0a82a870d20d1b3553b697913d1
|
File details
Details for the file saynow-0.1.0-py3-none-any.whl.
File metadata
- Download URL: saynow-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.17 {"installer":{"name":"uv","version":"0.9.17","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fee87660310b437a53dbf5a1e1d1759ec6a0c5c20e3149e1cdd5ed5ab2ee0fb0
|
|
| MD5 |
acc52eda03ad0874131acc75fe4a9e74
|
|
| BLAKE2b-256 |
3839d14bbd55dea341345c865fa15dc2df78f022c232d5ae706e37e32ab59b7a
|