🔔 Audible notifications for agentic CLI workflows (Rust)
Rust
0
7 commits
updated Feb 2, 2026
Audible notifications for agentic CLI workflows on macOS. Native Rust rewrite of agent-chime for faster startup and single-binary distribution.
When your agent yields or asks for a decision, you hear a short, clear audio cue (TTS or earcon) while still seeing the full text output.
| Aspect | Python (agent-chime) | Rust (agent-chime-rs) |
|---|---|---|
| Startup | ~500ms | ~10ms |
| Distribution | Requires Python + venv | Single binary |
| TTS Backend | mlx-audio | pocket-tts / qwen3-tts-rs |
| Memory | Higher (Python runtime) | Lower |
| Install | uv sync | cargo install or download binary |
yield and decision moments from agent toolsclaude, codex, opencodeclaude — Claude Code CLIcodex — OpenAI Codex CLIopencode — OpenCode CLIWe integrate community Rust ports built on Candle:
| Backend | Model Class | Speed | Use Case |
|---|---|---|---|
| pocket-tts | PocketTTS 0.5B | ~0.3x RT | Default — fast, CPU-friendly |
| qwen3-tts-rs | Qwen3-TTS 0.6B/1.7B | ~0.5-0.7x RT | VoiceDesign and higher‑quality voices |
Both are local-first and support CPU inference. Qwen3-TTS is heavy; CPU-only
runs are not recommended (expect high latency). Prefer Metal/CUDA acceleration
and build with --features metal. Repeated prompts are cached, so common event
phrases become near-instant after the first run.
# From crates.io (once published)
cargo install agent-chime
# Or build from source
git clone https://github.com/kevinmichaelchen/agent-chime-rs
cd agent-chime-rs
cargo build --release
Add to ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{ "type": "command", "command": "agent-chime notify --source claude" }
],
"Notification": [
{ "type": "command", "command": "agent-chime notify --source claude" }
],
"PreToolUse": [
{ "type": "command", "command": "agent-chime notify --source claude" }
]
}
}
Note: PreToolUse is only used for AskUserQuestion events; configure your
Claude hooks to forward those to agent-chime so decision prompts are audible.
Add to ~/.codex/config.toml:
notify = ["agent-chime", "notify", "--source", "codex"]
Create .opencode/plugin/agent-chime.js:
export const AgentChimePlugin = async ({ $ }) => ({
event: async ({ event }) => {
if (event.type === "session.idle")
await $`agent-chime notify --source opencode --event AGENT_YIELD`;
if (event.type === "session.error")
await $`agent-chime notify --source opencode --event ERROR_RETRY`;
if (event.type === "permission.asked")
await $`agent-chime notify --source opencode --event DECISION_REQUIRED`;
},
});
# Show system info and recommended TTS backend
agent-chime system-info
agent-chime system-info --json
# List available TTS models and cache status
agent-chime models
agent-chime models --json
# Test TTS synthesis
agent-chime test-tts
agent-chime test-tts --text "Hello world"
agent-chime test-tts --backend pocket-tts
agent-chime test-tts --backend qwen3-tts --instruct "A cheerful voice"
# Manage configuration
agent-chime config # Show config path
agent-chime config --show # Show current config as JSON
agent-chime config --init # Create default config file
agent-chime config --validate # Validate configuration
# Process notifications (called by hooks)
agent-chime notify --source claude # Reads JSON from stdin
agent-chime notify --source codex # Reads JSON from argv
agent-chime notify --source opencode --event AGENT_YIELD
agent-chime notify --source opencode --event AGENT_YIELD --summary "Build complete"
Create ~/.config/agent-chime/config.json:
{
"tts": {
"backend": "pocket-tts",
"voice": null,
"instruct": null,
"timeout_seconds": 10,
"allow_downloads": true,
"pocket_tts": {
"variant": "b6369a24",
"voice": "alba",
"use_metal": false
},
"qwen3_tts": {
"model": null,
"tokenizer": null,
"speaker": "Ryan",
"language": "English",
"ref_audio": null,
"ref_text": null,
"device": "auto"
}
},
"volume": 0.8,
"cache_max_mb": 100,
"cache_max_entries": 1000,
"voicepack": {
"enabled": false,
"manifest_path": "./voicepack/manifest.json",
"routes": [
{
"pattern": "error|failed|timeout",
"phrases": ["system.timeout_fallback"],
"events": ["ERROR_RETRY"]
}
]
},
"events": {
"AGENT_YIELD": {
"enabled": true,
"mode": "tts",
"template": "Ready."
},
"DECISION_REQUIRED": {
"enabled": true,
"mode": "tts",
"template": "I need your input."
},
"ERROR_RETRY": {
"enabled": true,
"mode": "earcon"
}
}
}
Audio is cached on disk (LRU by modification time) to speed up repeated prompts.
Tune cache_max_mb and cache_max_entries to fit your system. If synthesis
exceeds tts.timeout_seconds, the process is terminated and earcons are used
instead. Set timeout_seconds to 0 to disable the circuit breaker.
Enable voicepack.enabled to play pre-generated audio files instead of doing
on-device TTS. manifest_path should point at a voice pack manifest (see
agent-chime-voicepack-spec). When routes match the last message, agent-chime
will choose a phrase from those keys; otherwise it falls back to the event
mapping in the manifest.
If voicepack.enabled is true and a pack can be selected, playback happens
before any TTS or earcon fallback.
This repo ships a small dev voice pack under ./voicepack. Regenerate the
audio files with:
cargo run --example voicepack_gen
summary from the CLI payload (Claude/Codex). For OpenCode, pass
--summary to enable routing by message text.routes are configured, scan in order and pick the first regex match
that also matches the current event (if events is set).route.phrases. Otherwise,
fall back to manifest.events[<event>].Enable debug logs to see which phrase/file was selected:
agent-chime -v notify --source opencode --event AGENT_YIELD --summary "Build complete"
| Backend | Best For | Voice Options |
|---|---|---|
pocket-tts | Fast notifications, low memory | Predefined voices (e.g. alba) |
qwen3-tts | High quality, expressive speech | Speakers, voice cloning, VoiceDesign text |
For VoiceDesign speech with Qwen3-TTS:
agent-chime test-tts \
--backend qwen3-tts \
--text "I need your input on this decision." \
--instruct "A calm, professional voice with slight urgency"
Both backends can download model assets from Hugging Face when allow_downloads
is true. Set it to false if you require fully offline operation and provide
local paths for qwen3_tts.model (and optional qwen3_tts.tokenizer) and
pocket_tts.voice where needed. If the model repo requires authentication, set
HF_TOKEN in your environment.
To use Qwen3-TTS, point tts.qwen3_tts.model at a local model directory or a
Hugging Face model ID:
{
"tts": {
"backend": "qwen3-tts",
"allow_downloads": true,
"qwen3_tts": {
"model": "Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice",
"speaker": "Ryan",
"language": "English"
}
}
}
For Base models that support voice cloning, set qwen3_tts.ref_audio (WAV) and
optionally qwen3_tts.ref_text to enable ICL-style cloning.
| Event | Description | Default Mode |
|---|---|---|
AGENT_YIELD | Agent finished, waiting for user | TTS: "Ready." |
DECISION_REQUIRED | Agent needs explicit input | TTS: "I need your input." |
ERROR_RETRY | Recoverable error occurred | Earcon |
requirements.md — Functional and non-functional
requirementsdesign.md — Architecture, event model, and data flowclaude, codex, opencodePrereqs:
cmake (required by PocketTTS dependency)pre-commit (for prek hooks)dprint (for markdown formatting)# Debug build
cargo build
# Release build
cargo build --release
# With Metal acceleration (macOS)
cargo build --release --features metal
# Minimal build (disable heavy TTS backends)
cargo build --release --no-default-features
# Run tests
cargo test
# Run with logging
RUST_LOG=debug cargo run -- system-info
pre-commit install
pre-commit run --all-files
MIT
7 commits
Rust
100.0%
🔔 Audible notifications for agentic CLI workflows (Rust)
Rust
0
7 commits
updated Feb 2, 2026
Audible notifications for agentic CLI workflows on macOS. Native Rust rewrite of agent-chime for faster startup and single-binary distribution.
When your agent yields or asks for a decision, you hear a short, clear audio cue (TTS or earcon) while still seeing the full text output.
| Aspect | Python (agent-chime) | Rust (agent-chime-rs) |
|---|---|---|
| Startup | ~500ms | ~10ms |
| Distribution | Requires Python + venv | Single binary |
| TTS Backend | mlx-audio | pocket-tts / qwen3-tts-rs |
| Memory | Higher (Python runtime) | Lower |
| Install | uv sync | cargo install or download binary |
yield and decision moments from agent toolsclaude, codex, opencodeclaude — Claude Code CLIcodex — OpenAI Codex CLIopencode — OpenCode CLIWe integrate community Rust ports built on Candle:
| Backend | Model Class | Speed | Use Case |
|---|---|---|---|
| pocket-tts | PocketTTS 0.5B | ~0.3x RT | Default — fast, CPU-friendly |
| qwen3-tts-rs | Qwen3-TTS 0.6B/1.7B | ~0.5-0.7x RT | VoiceDesign and higher‑quality voices |
Both are local-first and support CPU inference. Qwen3-TTS is heavy; CPU-only
runs are not recommended (expect high latency). Prefer Metal/CUDA acceleration
and build with --features metal. Repeated prompts are cached, so common event
phrases become near-instant after the first run.
# From crates.io (once published)
cargo install agent-chime
# Or build from source
git clone https://github.com/kevinmichaelchen/agent-chime-rs
cd agent-chime-rs
cargo build --release
Add to ~/.claude/settings.json:
{
"hooks": {
"Stop": [
{ "type": "command", "command": "agent-chime notify --source claude" }
],
"Notification": [
{ "type": "command", "command": "agent-chime notify --source claude" }
],
"PreToolUse": [
{ "type": "command", "command": "agent-chime notify --source claude" }
]
}
}
Note: PreToolUse is only used for AskUserQuestion events; configure your
Claude hooks to forward those to agent-chime so decision prompts are audible.
Add to ~/.codex/config.toml:
notify = ["agent-chime", "notify", "--source", "codex"]
Create .opencode/plugin/agent-chime.js:
export const AgentChimePlugin = async ({ $ }) => ({
event: async ({ event }) => {
if (event.type === "session.idle")
await $`agent-chime notify --source opencode --event AGENT_YIELD`;
if (event.type === "session.error")
await $`agent-chime notify --source opencode --event ERROR_RETRY`;
if (event.type === "permission.asked")
await $`agent-chime notify --source opencode --event DECISION_REQUIRED`;
},
});
# Show system info and recommended TTS backend
agent-chime system-info
agent-chime system-info --json
# List available TTS models and cache status
agent-chime models
agent-chime models --json
# Test TTS synthesis
agent-chime test-tts
agent-chime test-tts --text "Hello world"
agent-chime test-tts --backend pocket-tts
agent-chime test-tts --backend qwen3-tts --instruct "A cheerful voice"
# Manage configuration
agent-chime config # Show config path
agent-chime config --show # Show current config as JSON
agent-chime config --init # Create default config file
agent-chime config --validate # Validate configuration
# Process notifications (called by hooks)
agent-chime notify --source claude # Reads JSON from stdin
agent-chime notify --source codex # Reads JSON from argv
agent-chime notify --source opencode --event AGENT_YIELD
agent-chime notify --source opencode --event AGENT_YIELD --summary "Build complete"
Create ~/.config/agent-chime/config.json:
{
"tts": {
"backend": "pocket-tts",
"voice": null,
"instruct": null,
"timeout_seconds": 10,
"allow_downloads": true,
"pocket_tts": {
"variant": "b6369a24",
"voice": "alba",
"use_metal": false
},
"qwen3_tts": {
"model": null,
"tokenizer": null,
"speaker": "Ryan",
"language": "English",
"ref_audio": null,
"ref_text": null,
"device": "auto"
}
},
"volume": 0.8,
"cache_max_mb": 100,
"cache_max_entries": 1000,
"voicepack": {
"enabled": false,
"manifest_path": "./voicepack/manifest.json",
"routes": [
{
"pattern": "error|failed|timeout",
"phrases": ["system.timeout_fallback"],
"events": ["ERROR_RETRY"]
}
]
},
"events": {
"AGENT_YIELD": {
"enabled": true,
"mode": "tts",
"template": "Ready."
},
"DECISION_REQUIRED": {
"enabled": true,
"mode": "tts",
"template": "I need your input."
},
"ERROR_RETRY": {
"enabled": true,
"mode": "earcon"
}
}
}
Audio is cached on disk (LRU by modification time) to speed up repeated prompts.
Tune cache_max_mb and cache_max_entries to fit your system. If synthesis
exceeds tts.timeout_seconds, the process is terminated and earcons are used
instead. Set timeout_seconds to 0 to disable the circuit breaker.
Enable voicepack.enabled to play pre-generated audio files instead of doing
on-device TTS. manifest_path should point at a voice pack manifest (see
agent-chime-voicepack-spec). When routes match the last message, agent-chime
will choose a phrase from those keys; otherwise it falls back to the event
mapping in the manifest.
If voicepack.enabled is true and a pack can be selected, playback happens
before any TTS or earcon fallback.
This repo ships a small dev voice pack under ./voicepack. Regenerate the
audio files with:
cargo run --example voicepack_gen
summary from the CLI payload (Claude/Codex). For OpenCode, pass
--summary to enable routing by message text.routes are configured, scan in order and pick the first regex match
that also matches the current event (if events is set).route.phrases. Otherwise,
fall back to manifest.events[<event>].Enable debug logs to see which phrase/file was selected:
agent-chime -v notify --source opencode --event AGENT_YIELD --summary "Build complete"
| Backend | Best For | Voice Options |
|---|---|---|
pocket-tts | Fast notifications, low memory | Predefined voices (e.g. alba) |
qwen3-tts | High quality, expressive speech | Speakers, voice cloning, VoiceDesign text |
For VoiceDesign speech with Qwen3-TTS:
agent-chime test-tts \
--backend qwen3-tts \
--text "I need your input on this decision." \
--instruct "A calm, professional voice with slight urgency"
Both backends can download model assets from Hugging Face when allow_downloads
is true. Set it to false if you require fully offline operation and provide
local paths for qwen3_tts.model (and optional qwen3_tts.tokenizer) and
pocket_tts.voice where needed. If the model repo requires authentication, set
HF_TOKEN in your environment.
To use Qwen3-TTS, point tts.qwen3_tts.model at a local model directory or a
Hugging Face model ID:
{
"tts": {
"backend": "qwen3-tts",
"allow_downloads": true,
"qwen3_tts": {
"model": "Qwen/Qwen3-TTS-12Hz-0.6B-CustomVoice",
"speaker": "Ryan",
"language": "English"
}
}
}
For Base models that support voice cloning, set qwen3_tts.ref_audio (WAV) and
optionally qwen3_tts.ref_text to enable ICL-style cloning.
| Event | Description | Default Mode |
|---|---|---|
AGENT_YIELD | Agent finished, waiting for user | TTS: "Ready." |
DECISION_REQUIRED | Agent needs explicit input | TTS: "I need your input." |
ERROR_RETRY | Recoverable error occurred | Earcon |
requirements.md — Functional and non-functional
requirementsdesign.md — Architecture, event model, and data flowclaude, codex, opencodePrereqs:
cmake (required by PocketTTS dependency)pre-commit (for prek hooks)dprint (for markdown formatting)# Debug build
cargo build
# Release build
cargo build --release
# With Metal acceleration (macOS)
cargo build --release --features metal
# Minimal build (disable heavy TTS backends)
cargo build --release --no-default-features
# Run tests
cargo test
# Run with logging
RUST_LOG=debug cargo run -- system-info
pre-commit install
pre-commit run --all-files
MIT
7 commits
Rust
100.0%