Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ ELEVENLABS_API_KEY=
ELEVENLABS_VOICE_ID=21m00Tcm4TlvDq8ikWAM # Default: Rachel
ELEVENLABS_MODEL_ID=eleven_flash_v2_5 # Options: eleven_turbo_v2_5, eleven_multilingual_v2

# 60db API Configuration (optional, for cloud-based Voice AI TTS)
# Get your API key from https://60db.ai/
SIXTYDB_API_KEY=
SIXTYDB_VOICE_ID=fbb75ed2-975a-40c7-9e06-38e30524a9a1 # Default: Zara
SIXTYDB_MODEL_ID= # Empty = voice's default tier
SIXTYDB_SPEED=1.0 # 0.5-2.0
SIXTYDB_STABILITY=50 # 0-100
SIXTYDB_SIMILARITY=75 # 0-100

# Future: Additional TTS providers
# GOOGLE_TTS_API_KEY=
# AZURE_TTS_API_KEY=
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- 60db as an additional cloud TTS provider alongside ElevenLabs
- 60db option in the interactive installer (`ccnotify install`)
- `SixtyDBProvider` in the TTS provider factory (alias: `sixtydb`)
- `SIXTYDB_*` environment variables for runtime configuration
- Embedded 60db and ElevenLabs providers in the standalone notify hook script
- 60db section in default configuration and `.env.example`

## [0.1.13] - 2025-08-10

### Fixed
Expand Down
20 changes: 18 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ uvx ccnotify install

That's it! The installer will:

- Help you choose between local (Kokoro) or cloud (ElevenLabs) TTS
- Help you choose between local (Kokoro) or cloud (ElevenLabs, 60db) TTS
- Download models if needed
- Install hooks into Claude Code
- Create a configuration file
Expand Down Expand Up @@ -66,7 +66,7 @@ If you use Elevenlabs as a TTS provider, [use my Affiliate link to signup](https
## Features

- **Smart Filtering**: Only notifies for significant events, not routine operations
- **Dual TTS Options**: Local Kokoro models (50+ voices) or ElevenLabs cloud API
- **Dual TTS Options**: Local Kokoro models (50+ voices) or cloud APIs (ElevenLabs, 60db)
- **Project Detection**: Automatically identifies your current project
- **Zero Config**: Works out of the box with sensible defaults
- **Privacy First**: Local processing with optional cloud TTS
Expand Down Expand Up @@ -102,10 +102,25 @@ The installer creates a configuration file at `~/.claude/ccnotify/config.json`.
}
```

**For Cloud TTS (60db):**

```json
{
"tts_provider": "60db",
"api_key": "your_api_key_here",
"voice_id": "fbb75ed2-975a-40c7-9e06-38e30524a9a1",
"model_id": "",
"speed": 1.0,
"stability": 50,
"similarity": 75
}
```

**Voice Options:**

- **Kokoro**: `af_heart` (recommended), `af_sarah`, `am_adam`, `bf_alice`, `bm_daniel` and [40+ others](https://github.com/thewh1teagle/kokoro-onnx)
- **ElevenLabs**: Use voice IDs from your [ElevenLabs account](https://try.elevenlabs.io/ist8m7h95ed2)
- **60db**: Use voice IDs from your [60db account](https://60db.ai/) (`stability` and `similarity` use a 0-100 scale)

**Pronunciation Customization:**

Expand Down Expand Up @@ -159,6 +174,7 @@ Customize how project names and commands are pronounced by editing `~/.claude/cc
- Claude Code CLI
- **For Kokoro**: ~350MB disk space for local models
- **For ElevenLabs**: API account and internet connection
- **For 60db**: API account and internet connection

## Early Version Notice

Expand Down
7 changes: 7 additions & 0 deletions src/ccnotify/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,13 @@ def get_default_config() -> Dict[str, Any]:
"stability": 0.5,
"similarity_boost": 0.5,
},
"sixtydb": {
"voice_id": "fbb75ed2-975a-40c7-9e06-38e30524a9a1",
"model_id": "",
"speed": 1.0,
"stability": 50,
"similarity": 75,
},
"notifications": {"enabled": True, "sound_enabled": True, "logging_enabled": False},
"replacements": {"enabled": True, "auto_add_projects": True},
}
Expand Down
22 changes: 21 additions & 1 deletion src/ccnotify/installer/detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ class ModelStatus:
kokoro_downloaded: bool = False
kokoro_models_count: int = 0
elevenlabs_configured: bool = False
sixtydb_configured: bool = False
issues: List[str] = None

def __post_init__(self):
Expand Down Expand Up @@ -85,7 +86,9 @@ def check_existing_installation(self) -> InstallationStatus:
# Check models
model_status = self.get_model_status()
status.models_downloaded = (
model_status.kokoro_downloaded or model_status.elevenlabs_configured
model_status.kokoro_downloaded
or model_status.elevenlabs_configured
or model_status.sixtydb_configured
)

# Collect issues
Expand Down Expand Up @@ -156,6 +159,9 @@ def get_config_status(self) -> ConfigStatus:
if status.provider == "elevenlabs":
if not config.get("elevenlabs_api_key"):
status.issues.append("ElevenLabs provider selected but no API key configured")
elif status.provider == "60db":
if not config.get("sixtydb_api_key"):
status.issues.append("60db provider selected but no API key configured")

except json.JSONDecodeError:
status.issues.append("Config file exists but contains invalid JSON")
Expand Down Expand Up @@ -201,6 +207,20 @@ def get_model_status(self) -> ModelStatus:
except Exception:
status.issues.append("Could not verify ElevenLabs configuration")

elif status.provider == "60db":
# Check 60db configuration
try:
with open(self.ccnotify_dir / "config.json") as f:
config = json.load(f)

if config.get("sixtydb_api_key"):
status.sixtydb_configured = True
else:
status.issues.append("60db provider selected but no API key configured")

except Exception:
status.issues.append("Could not verify 60db configuration")

return status

def _check_hooks_configured(self) -> bool:
Expand Down
27 changes: 25 additions & 2 deletions src/ccnotify/installer/flows.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,13 +216,14 @@ def _setup_tts_provider(self, quiet: bool = False) -> Optional[Dict[str, Any]]:

table.add_row("1", "Kokoro", "Local AI models (recommended, privacy-focused)")
table.add_row("2", "ElevenLabs", "Cloud-based premium quality (requires API key)")
table.add_row("3", "None", "Silent mode (no voice notifications)")
table.add_row("3", "60db", "Cloud-based Voice AI TTS (requires API key)")
table.add_row("4", "None", "Silent mode (no voice notifications)")

console.print(table)
console.print()

while True:
choice = Prompt.ask("Select TTS provider", choices=["1", "2", "3"], default="1")
choice = Prompt.ask("Select TTS provider", choices=["1", "2", "3", "4"], default="1")

if choice == "1":
# Kokoro setup
Expand Down Expand Up @@ -251,6 +252,16 @@ def _setup_tts_provider(self, quiet: bool = False) -> Optional[Dict[str, Any]]:
return elevenlabs_config

elif choice == "3":
# 60db setup
sixtydb_config = self._setup_sixtydb()
if not sixtydb_config:
console.print(
"[yellow]60db setup failed. Please choose another provider.[/yellow]"
)
continue
return sixtydb_config

elif choice == "4":
# Silent mode
return {"tts_provider": "none"}

Expand All @@ -266,6 +277,18 @@ def _setup_elevenlabs(self) -> Optional[Dict[str, Any]]:
# TODO: Validate API key
return {"tts_provider": "elevenlabs", "elevenlabs_api_key": api_key}

def _setup_sixtydb(self) -> Optional[Dict[str, Any]]:
"""Setup 60db TTS provider."""
console.print("\n[bold cyan]Setting up 60db TTS...[/bold cyan]")

api_key = Prompt.ask("Enter your 60db API key", password=True)
if not api_key:
console.print("[red]API key required for 60db[/red]")
return None

# TODO: Validate API key
return {"tts_provider": "60db", "sixtydb_api_key": api_key}

def _install_script_and_config(self, provider_config: Dict[str, Any]) -> bool:
"""Install ccnotify.py script and configuration."""
try:
Expand Down
Loading