Metadata-Version: 2.4
Name: yappai-tts
Version: 0.1.0
Summary: An AI that yaps at you. Voice notifications for Claude Code, Claude desktop, and Cowork on macOS.
Author: Thu Min
License: MIT
Project-URL: Homepage, https://github.com/Thu-Min/yappai
Project-URL: Repository, https://github.com/Thu-Min/yappai
Project-URL: Issues, https://github.com/Thu-Min/yappai/issues
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: edge-tts>=6.1
Provides-Extra: fallback
Requires-Dist: pyobjc-core>=10; extra == "fallback"
Provides-Extra: elevenlabs
Requires-Dist: elevenlabs>=1.0; extra == "elevenlabs"
Dynamic: license-file

# 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`](https://stedolan.github.io/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

```bash
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:

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

### 4. Verify

```bash
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](#configuration)) and that you're not muted (`yappai status`).

## Day-to-day

```bash
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:

```bash
yappai config set personality=happy
```

See [`personalities/README.md`](personalities/README.md) for the file format. Quick recipe:

```markdown
# 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`:

```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

```bash
./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](https://elevenlabs.io/) 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](https://elevenlabs.io/) and grab an API key from your profile settings.
2. Pick a voice ID from your ElevenLabs voice library.
3. Configure yappai:

```bash
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](https://github.com/emaspa/claudevoice-macos) — same family of problems, different multi-source architecture.
