Skip to main content

An AI that yaps at you. Voice notifications for Claude Code, Claude desktop, and Cowork on macOS.

Project description

YappAI

An AI that yaps at you.

A unified voice notification system for Anthropic's tools on macOS. Hear when Claude Code finishes a task, when the Claude desktop app needs your attention, or when Cowork wraps up — in any voice personality you like.

$ # Claude Code finishes a long task
🔊 "All sorted, sir."

$ # Claude Code pauses for permission
🔊 "Sir, your input is required."

$ # Switch personality, no code changes
$ yappai config set personality=happy
$ # next event:
🔊 "Yay, task complete!"

What it watches

Source Mechanism
Claude Code CLI Official hook events (UserPromptSubmit, Stop, Notification)
Claude desktop macOS Notification Center DB polling (Full Disk Access)
Cowork macOS Notification Center DB polling (Full Disk Access)

macOS only. Python 3.10+. macOS 14 (Sonoma) or newer.

Install

Prerequisites

  • macOS 14 (Sonoma) or newer
  • Python 3.10+ (python3 --version)
  • ~/.local/bin on your PATH (the installer warns if it isn't — see step 3 below)
  • Optional: jq for a cleaner Claude Code settings merge (brew install jq). If absent, the installer falls back to a Python merge that does the same thing.

1. Clone and run the installer

git clone https://github.com/Thu-Min/yappai.git
cd yappai
./install.sh

What the installer does:

  1. Copies the package to ~/.local/share/yappai/ and creates a venv there
  2. Installs a yappai shim at ~/.local/bin/yappai
  3. Writes default config to ~/.config/yappai/config.json
  4. Detects which Anthropic apps are installed (Claude Code CLI, Claude desktop, Cowork) and enables only those sources
  5. Merges three hook entries (UserPromptSubmit, Stop, Notification) into ~/.claude/settings.json — your existing settings are backed up as settings.json.bak.<ts>
  6. Installs and loads a launchd agent (com.yappai.watcher) that polls the macOS Notification Center DB for Claude desktop / Cowork events
  7. Probes Full Disk Access and prints instructions if it's missing
  8. Plays a final test yap so you know it's wired up

2. Grant Full Disk Access (one-time)

The desktop/Cowork watcher reads ~/Library/Group Containers/group.com.apple.usernoted/db2/db, which macOS guards behind FDA. If the installer's probe failed:

  1. Open System Settings → Privacy & Security → Full Disk Access
  2. Click +, press ⌘⇧G, and paste: ~/.local/share/yappai/.venv/bin
  3. Add python3 (or python3.x) from that folder
  4. Reload the watcher: launchctl kickstart -k gui/$UID/com.yappai.watcher

Skip this step if you only want Claude Code yaps — those go through hooks and don't need FDA.

3. Make sure ~/.local/bin is on PATH

If the installer warned that it isn't:

# zsh (default on macOS)
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

4. Verify

yappai test --kind stop --summary "the integration is wired up"
yappai test --kind needs_input
yappai test --personality happy --kind stop
yappai watcher check         # should print {"ok": true, ...}
yappai status                # full state dump

If you don't hear anything: check that you're not in a "skip when frontmost" terminal (see unfocused_skip_apps in Configuration) and that you're not muted (yappai status).

Day-to-day

yappai mute                          # silence yappai
yappai unmute
yappai status                        # config + watcher state
yappai list-personalities
yappai list-voices --locale en       # browse edge-tts voices
yappai say "Hello sir"               # one-off
yappai say "Hi" --personality happy

yappai config show
yappai config set personality=jarvis
yappai config set summary_max_chars=80
yappai config set only_when_unfocused=false

yappai watcher start
yappai watcher stop
yappai watcher restart
yappai watcher run --once            # run one poll iteration in foreground
yappai watcher check                 # probe Notification DB access

Personality system

Each .md file in personalities/ is a separate persona. Drop in a new one — no code changes — and switch:

yappai config set personality=happy

See personalities/README.md for the file format. Quick recipe:

# Pirate

## voice
edge-tts: en-GB-RyanNeural

## completions
- Arr! Done, captain. {summary}
- Yo ho, that be finished.

## needs_input
- Captain on deck! Yer needed.

Missing sections fall back to jarvis.md.

Configuration

~/.config/yappai/config.json:

{
  "personality": "jarvis",
  "default_engine": "edge-tts",
  "elevenlabs_api_key": null,
  "enabled_sources": ["claude_code", "claude_desktop", "cowork"],
  "muted": false,
  "summary_max_chars": 120,
  "dedupe_window_seconds": 3.0,
  "only_when_unfocused": true,
  "claude_desktop_bundle_id": "com.anthropic.claudefordesktop",
  "cowork_bundle_id": null,
  "unfocused_skip_apps": ["iTerm2", "Terminal", "Warp", "Ghostty", "Claude", "Cowork"]
}

only_when_unfocused: true means yappai stays quiet when one of these apps is frontmost — no point yapping when you're already looking at the terminal.

Full Disk Access

The watcher reads ~/Library/Group Containers/group.com.apple.usernoted/db2/db, which macOS guards behind FDA. The installer probes this and prints instructions if it fails. Manual fix:

  1. System Settings → Privacy & Security → Full Disk Access
  2. Add the binary that runs the watcher — for the launchd agent that's ~/.local/share/yappai/.venv/bin/python3 (or the symlinked target). For ad-hoc terminal testing, add your terminal app.
  3. Reload: launchctl kickstart -k gui/$UID/com.yappai.watcher

If FDA is too painful, see CLAUDE.md for the Plan B (PyObjC UNUserNotificationCenter listener).

Uninstall

./uninstall.sh           # keeps your config
./uninstall.sh --purge   # nukes config too

The Claude Code settings file gets restored from the most recent .bak.<ts> if present, otherwise yappai entries are stripped surgically. Other users' hooks are preserved.

How it sounds

Default Jarvis voice is en-GB-RyanNeural (edge-tts, free). That's what you get out of the box, no setup, no account.

Optional: ElevenLabs (bring your own key)

If you want a more theatrical voice — proper Jarvis tones, custom cloned voices, etc. — yappai can route through ElevenLabs instead. You'll need your own API key (yappai does not ship one, and your key never leaves your machine):

  1. Sign up at elevenlabs.io and grab an API key from your profile settings.
  2. Pick a voice ID from your ElevenLabs voice library.
  3. Configure yappai:
yappai config set elevenlabs_api_key=sk_your_key_here
# then in any personality .md file, set:
#   elevenlabs: <voice_id>
# instead of the edge-tts line.

Or, if you'd rather not touch the CLI, paste this prompt into Claude Code from the yappai folder and let it do the wiring:

I have an ElevenLabs API key (sk_...) and the voice ID <paste_voice_id>. Please add the key to my ~/.config/yappai/config.json, then update personalities/jarvis.md to use that ElevenLabs voice instead of edge-tts. Don't print the key back to me.

The integration code lives in yappai/tts.py. Free tier is fine for casual use; check ElevenLabs' character limits for your plan.

Acknowledgements

Heavily inspired by emaspa/claudevoice-macos — same family of problems, different multi-source architecture.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

yappai_tts-0.1.0.tar.gz (24.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

yappai_tts-0.1.0-py3-none-any.whl (25.1 kB view details)

Uploaded Python 3

File details

Details for the file yappai_tts-0.1.0.tar.gz.

File metadata

  • Download URL: yappai_tts-0.1.0.tar.gz
  • Upload date:
  • Size: 24.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for yappai_tts-0.1.0.tar.gz
Algorithm Hash digest
SHA256 60bcd44311763315f2e5dff296219b7a3ec565f880b19fd4cd51fdaf98757300
MD5 9cb4a7e2d9a66838f9ceedfa02b48331
BLAKE2b-256 edde9c8ee5aa64a433e556ff0413803f090613aabce182316b92646d8e6537ad

See more details on using hashes here.

File details

Details for the file yappai_tts-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: yappai_tts-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 25.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for yappai_tts-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3b960df963bb458fa9661e33464f457ae1591b1f4652b29c451672f974e024d7
MD5 8349b5b152ab7379f005460387a720dc
BLAKE2b-256 9702269d42b6183e0df440513cf3167427f65ba82fa275a7067aa42510a8857b

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page