Skip to content

v0.1.2 — multilingual picker + voice resolver fix

Latest

Choose a tag to compare

@Quigleybits Quigleybits released this 18 May 19:13

cctts v0.1.2

Patch release. The full edge-tts voice catalogue — roughly 322 voices across 70+ languages — is now accessible through a three-stage interactive picker and a magic phrase. In 0.1.1 the voice system was limited to 9 hand-picked English voices; 0.1.2 opens the rest. This release also corrects cross-scope voice precedence so that "last command wins" holds across every terminal / project / global combination.


The centrepiece is /cctts:voice, which now walks through a three-stage picker: first you select a language (e.g. "French", "Japanese", "Welsh"), then a locale if the language has multiple regional variants (fr-FR vs fr-CA, for instance), and finally the voice itself from the short list of voices available for that locale. The picker assigns the chosen voice to the current terminal only — to set it globally or per-project, reach for the magic-phrase forms (cctts -g -voice <name> or cctts -p -voice <name>) once you know which voice you want. The picker never leaves you staring at 320+ names at once; at no stage does the list exceed a handful of options.

Alongside the picker, cctts -voice <name> lets you set a voice by name in one shot when you already know what you want. The usual scope flags apply: cctts -g -voice en-US-AndrewMultilingualNeural assigns it globally, cctts -p -voice ... assigns it to the current project. For quick audition before committing, cctts -test "hello from Andrew" -voice en-US-AndrewMultilingualNeural speaks the sample text through that voice without touching your current state. One argparse quirk to note: the test text must appear before the -voice flag — cctts -test "text" -voice <name> works, but -voice <name> -test "text" treats the voice name as the test string.

The voices.md catalogue that ships with the plugin grows from 9 to 20 slots, adding 11 new voices spanning French, Spanish, Japanese, German, Portuguese (Brazil), Chinese Mandarin, Arabic, and a second Welsh voice. Slots 10–20 are intentionally manual-only — the auto-voice pool (the 9 voices cctts randomly assigns on first install) stays at 9 by design; the new slots are there for users who want to reach further into the catalogue by choice, not on random assignment. A new cli/gen_all_voices.py script regenerates the full catalogue listing by querying edge-tts directly, useful if Microsoft adds or retires voices between cctts releases.

Cross-scope voice precedence (the resolver fix)

Picker and named voices are stored under a new voice_name field; numeric slot picks (cctts -3) still use voice_index. The two fields could conflict across scopes — and in early picker testing they did. cctts -g -voice X followed by cctts -3 in a terminal should speak slot 3 (per the documented terminal > project > global precedence and the "last command wins" intuition), but the early resolver merged the two fields independently, leaving the global voice_name to shadow the terminal voice_index.

The resolver in 0.1.2 treats (voice_name, voice_index) as a single unit per scope: it walks terminal → project → auto-voice → global, and the first scope that has either field set wins the pair. Broadcasts (-g -voice X, -p -voice X) now also clear subordinate-scope voice_index, symmetric with how slot broadcasts have always cleared subordinate voice_name. Same-scope writes clear the sibling field too — so each scope holds at most one of the two, and cctts -status is honest about what's actually set. Net effect: the precedence table in the README behaves exactly as documented, no surprises.

State-file compatibility is fully maintained. The schema gains an optional voice_name field at every scope layer (global, project, per-terminal), but it is nullable — existing 0.1.1 state files load cleanly with voice_name=null, and all lookup logic falls back to the previous voice_index path when voice_name is absent. Upgrading does not require a migration step or a state file wipe.

The features deferred from 0.1.0 — persistent warm worker, audio ducking, alt backends (ElevenLabs/Kokoro), cross-tool support (Codex/Cursor), schema v1 drop — remain deferred. None of them shipped in 0.1.2; they carry forward to 0.3+.

Upgrading

In any Claude Code session, run /plugin update cctts@quigleybits to pick up v0.1.2. Fully restart Claude Code afterward (close + reopen) — /reload-plugins alone is not sufficient to hot-swap hook code. No state migration is needed; your current voice, rate, and scope settings carry forward unchanged.

The new surface at a glance:

  • /cctts:voice → three-stage language → locale → voice picker (replaces the flat 9-name list)
  • cctts -voice <name> → set voice by name in current terminal scope
  • cctts -g -voice <name> / cctts -p -voice <name> → set voice globally or per-project
  • cctts -test "text" -voice <name> → audition a voice without changing state (text arg must precede -voice)
  • voices.md → 20-slot catalogue (was 9)
  • Cross-scope voice picks now follow terminal > project > auto-voice > global strictly — last command wins.