vocalize-cli 0.10.2__tar.gz → 0.12.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/CHANGELOG.md +198 -0
- vocalize_cli-0.10.2/README.md → vocalize_cli-0.12.0/PKG-INFO +142 -45
- vocalize_cli-0.10.2/PKG-INFO → vocalize_cli-0.12.0/README.md +106 -81
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/dictation.md +28 -14
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/installation.md +14 -1
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/choreography.md +110 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/decisions.md +509 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/design.md +355 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/plan.md +331 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/review-0.12.0.md +142 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/review-plan-2026-09-06.md +41 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/project-plan.md +76 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/report.md +16 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/task-report.md +30 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/validate-exit.sh +134 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-10-release-0-13-0/project-plan.md +50 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-10-release-0-13-0/validate-exit.sh +124 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-11-cue-hold-paste/project-plan.md +52 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-11-cue-hold-paste/validate-exit.sh +125 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-11b-playback-pause/project-plan.md +54 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-11b-playback-pause/validate-exit.sh +128 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-12-release-0-13-1/project-plan.md +47 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-12-release-0-13-1/validate-exit.sh +122 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-13-spikes/project-plan.md +45 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-13-spikes/validate-exit.sh +114 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-14-local-llm/project-plan.md +50 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-14-local-llm/validate-exit.sh +124 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-15-notes/project-plan.md +48 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-15-notes/validate-exit.sh +111 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-15b-recording-pause/project-plan.md +57 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-15b-recording-pause/validate-exit.sh +131 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-16-release-0-14-0/project-plan.md +50 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-16-release-0-14-0/validate-exit.sh +124 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-17-optional-spikes/project-plan.md +44 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-17-optional-spikes/validate-exit.sh +118 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/project-plan.md +49 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/report.md +16 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/task-report.md +35 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/validate-exit.sh +113 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/project-plan.md +55 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/report.md +20 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/task-report.md +36 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/validate-exit.sh +152 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-4-keychain/project-plan.md +80 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-4-keychain/report.md +16 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-4-keychain/task-report.md +34 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-4-keychain/validate-exit.sh +134 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-5-keys-tab/project-plan.md +47 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-5-keys-tab/report.md +24 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-5-keys-tab/task-report.md +31 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-5-keys-tab/validate-exit.sh +118 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-6-release-0-12-0/project-plan.md +50 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-6-release-0-12-0/validate-exit.sh +124 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-7-hotkey-spike-and-builder/project-plan.md +45 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-7-hotkey-spike-and-builder/validate-exit.sh +113 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-8a-app-swift/project-plan.md +44 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-8a-app-swift/validate-exit.sh +115 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-8b-app-python/project-plan.md +49 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-8b-app-python/validate-exit.sh +119 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-9-doctor-integrate-setup/project-plan.md +48 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-9-doctor-integrate-setup/validate-exit.sh +121 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/spike-notes.md +38 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/split-assessment.md +57 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/verification.md +216 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/decisions.md +471 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/design.md +127 -1
- vocalize_cli-0.12.0/docs/plans/2026-09-next-features/review-0.11.0.md +134 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-10-release-0-11-0/report.md +160 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-10-release-0-11-0/validate-exit.sh +31 -2
- vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-7-portal-read/report.md +297 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-7-portal-read/review-findings.md +130 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-7-portal-read/validate-exit.sh +8 -1
- vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-8-portal-write/report.md +346 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-8-portal-write/validate-exit.sh +29 -2
- vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-9-portal-page/report.md +231 -0
- vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-9-portal-page/review-findings.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-9-portal-page/validate-exit.sh +25 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/provider-credentials.md +72 -0
- vocalize_cli-0.12.0/docs/research/2026-09-04-app-roadmap-analysis.md +290 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/roadmap.md +4 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/speak_options.py +4 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/pyproject.toml +2 -2
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/conftest.py +38 -0
- vocalize_cli-0.12.0/tests/portal_page_harness.js +814 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_auth.py +216 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_chain.py +39 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_cli.py +306 -5
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_config.py +186 -7
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_dictate.py +92 -114
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_elevenlabs_provider.py +1 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_http.py +19 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_listen_check.py +3 -2
- vocalize_cli-0.12.0/tests/test_llm.py +491 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_local_install.py +4 -1
- vocalize_cli-0.12.0/tests/test_portal.py +4119 -0
- vocalize_cli-0.12.0/tests/test_portal_assets.py +405 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_readiness.py +49 -15
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_speak_options.py +12 -0
- vocalize_cli-0.12.0/tests/test_tts.py +411 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_whisper_manifest.py +22 -5
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_whisper_worker.py +73 -2
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_wizard.py +399 -3
- vocalize_cli-0.12.0/uv.lock +1571 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/__init__.py +2 -2
- vocalize_cli-0.12.0/vocalize/assets/portal.html +295 -0
- vocalize_cli-0.12.0/vocalize/assets/portal.js +1595 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/auth.py +192 -9
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/chain.py +21 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/cli.py +103 -31
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/config.py +192 -17
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/dictate.py +29 -77
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/exceptions.py +15 -0
- vocalize_cli-0.12.0/vocalize/llm.py +315 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/install.py +10 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/whisper_manifest.py +11 -2
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/whisper_worker.py +39 -2
- vocalize_cli-0.12.0/vocalize/portal.py +1805 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/_http.py +9 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/kokoro.py +2 -1
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/readiness.py +32 -7
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/tts.py +44 -5
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/wizard.py +189 -17
- vocalize_cli-0.10.2/tests/test_tts.py +0 -201
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/.env.example +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/.github/workflows/ci.yml +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/.gitignore +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/LICENSE +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/next-features-analysis.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/choreography.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/review-0.10.0.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-1-status/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-1-status/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-1-status/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-10-release-0-11-0/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-2-stt-runtime/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-2-stt-runtime/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-2-stt-runtime/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-3-recorder/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-3-recorder/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-3-recorder/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-4-dictation/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-4-dictation/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-4-dictation/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-5-resume/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-5-resume/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-5-resume/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-6-release-0-10-0/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-6-release-0-10-0/report.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-6-release-0-10-0/validate-exit.sh +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-7-portal-read/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-8-portal-write/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-9-portal-page/project-plan.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/spike-2026-09-01.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/split-assessment.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/verification.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/research/2026-09-01-config-portal-design.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/research/2026-09-01-dictation-design.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/research/2026-09-01-voicebox-findings.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/claude_stop_hook.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/install_hook.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/install_quick_action.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Dictate with Vocalize.workflow/Contents/Info.plist +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Dictate with Vocalize.workflow/Contents/Resources/document.wflow +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Speak Latest Plan.workflow/Contents/Info.plist +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Speak Latest Plan.workflow/Contents/Resources/document.wflow +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Info.plist +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Resources/document.wflow +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Stop Vocalize.workflow/Contents/Info.plist +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Stop Vocalize.workflow/Contents/Resources/document.wflow +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/speak_url_gate.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_audio.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_cache.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_claude_stop_hook.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_clipboard.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_cue_assets.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_exceptions.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_google_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_install_hook.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_install_quick_action.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_kokoro_manifest.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_kokoro_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_kokoro_worker.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_ledger.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_openai_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_polly_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_preprocess.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_providers_registry.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_recorder_build.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_say_provider.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_speak_url_gate.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_uv_path.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/__main__.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/assets/cues/README.md +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/assets/cues/ready.wav +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/assets/cues/start.wav +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/assets/cues/stopped.wav +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/audio.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/cache.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/clipboard.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/interrupted.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/ledger.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/__init__.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/kokoro_manifest.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/kokoro_worker.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/preprocess.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/__init__.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/elevenlabs.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/google.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/openai.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/polly.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/say.py +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/recorder/Info.plist.in +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/recorder/Recorder.entitlements +0 -0
- {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/recorder/VocalizeRecorder.swift +0 -0
|
@@ -3,6 +3,204 @@
|
|
|
3
3
|
All notable changes to this project are documented here. Format follows
|
|
4
4
|
[Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## Unreleased
|
|
7
|
+
|
|
8
|
+
Nothing yet.
|
|
9
|
+
|
|
10
|
+
## 0.12.0 - 2026-09-07
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- **The default chain is now `kokoro`, then `say`.** A config with no
|
|
15
|
+
`chain` used to try ElevenLabs first; it now tries the on-device voice
|
|
16
|
+
first and falls back to macOS `say`. When `say` speaks because the Kokoro
|
|
17
|
+
model was never installed, the fallback line says so once and names the
|
|
18
|
+
fix: `vocalize local install`. Users who never set a chain hear `say`
|
|
19
|
+
after upgrading until they run that install; anyone with an explicit
|
|
20
|
+
`chain` sees no change. `vocalize usage`, `vocalize status` and the portal
|
|
21
|
+
now list the local providers first.
|
|
22
|
+
- **Dictation decodes with beam search.** whisper.cpp ran with its greedy
|
|
23
|
+
decoder, which merged words on fast speech ("toget" for "to get",
|
|
24
|
+
[#4](https://github.com/matthager12-collab/vocalize/issues/4)). The worker
|
|
25
|
+
now uses beam search with five beams, whisper.cpp's own default for that
|
|
26
|
+
strategy. `[stt] beam_size` (1–8) is the escape hatch: `1` restores the
|
|
27
|
+
greedy decoder if a take is slow to land on your machine. Honest result:
|
|
28
|
+
on the owner's own voice both decoders still produced "themerge" for
|
|
29
|
+
"the merge", so beam search improves decoding but does not close #4;
|
|
30
|
+
that issue stays open. The cost was measured on a 43 s synthetic clip
|
|
31
|
+
and recorded in `docs/plans/2026-09-app-roadmap/spike-notes.md`.
|
|
32
|
+
|
|
33
|
+
- **The default dictation model is now `large-v3-turbo-q5_0`.** On the
|
|
34
|
+
owner's own voice it was the first model to keep "the merge" as two
|
|
35
|
+
words ([#4](https://github.com/matthager12-collab/vocalize/issues/4));
|
|
36
|
+
beam search alone did not. It is no slower than `small.en` on an M4 and
|
|
37
|
+
uses about 90 MB more. **Upgrade note:** if your `[stt]` table sets no
|
|
38
|
+
`model`, dictation looks for the new default — run
|
|
39
|
+
`vocalize local install --stt` once (547 MB), or set
|
|
40
|
+
`model = "small.en"` to keep the lighter model.
|
|
41
|
+
- **`[stt] cleanup` names where the cleanup pass runs.** It was `true` or
|
|
42
|
+
`false`; it is now `off`, `claude-cli`, `anthropic` or `local` (`local`
|
|
43
|
+
is accepted today and honoured from 0.14.0). Older `true` / `false`
|
|
44
|
+
values keep working. The pass lives in a new `vocalize/llm.py`, one seam
|
|
45
|
+
for every language-model call, and the default prompt now also drops
|
|
46
|
+
restatements, false starts and filler
|
|
47
|
+
([#3](https://github.com/matthager12-collab/vocalize/issues/3)); say
|
|
48
|
+
"verbatim" as the first word of a take, pass `--verbatim`, or set
|
|
49
|
+
`[stt] verbatim = true` to keep every word.
|
|
50
|
+
- **Every cloud send is visible.** One stderr line, `vocalize: sent to
|
|
51
|
+
<backend>`, prints immediately before text leaves the machine and never
|
|
52
|
+
otherwise; the clipboard notification for a cleaned take says "cleaned
|
|
53
|
+
up by Claude — sent off this Mac".
|
|
54
|
+
- **`claude -p` sessions are tighter.** The cleanup pass and the plan-
|
|
55
|
+
speaking hook now pass `--strict-mcp-config`, run from the system
|
|
56
|
+
temporary directory, and the cleanup pass excludes your own hooks,
|
|
57
|
+
skills and `CLAUDE.md` (`--setting-sources ""`) and strips any stored
|
|
58
|
+
Anthropic key from the child's environment.
|
|
59
|
+
- **Provider settings are type-checked** (issue
|
|
60
|
+
[#5](https://github.com/matthager12-collab/vocalize/issues/5)): a
|
|
61
|
+
`voice`, `model`, `engine`, `language`, `region` or `profile` that is not
|
|
62
|
+
a short printable string is refused with a message naming the file and
|
|
63
|
+
the key, in the CLI and the portal alike.
|
|
64
|
+
- **Sentences no longer run together with the turbo models.** Their
|
|
65
|
+
segments arrive without a leading space and the worker joined them with
|
|
66
|
+
nothing ("working.I want"); segments are now joined with one space.
|
|
67
|
+
|
|
68
|
+
### Fixed
|
|
69
|
+
|
|
70
|
+
- **A stored key is readable from every Python on the Mac.** macOS pins a
|
|
71
|
+
keychain item to the binary that created it, so a key stored from the
|
|
72
|
+
terminal could be invisible, or behind an "Allow" dialog, when Claude
|
|
73
|
+
Code's shell or an upgraded vocalize asked for it. Keys are now written
|
|
74
|
+
and read through Apple's own `security` tool (secret on stdin, never on a
|
|
75
|
+
command line), which is the same accessing application whatever spawned
|
|
76
|
+
it; the item also records the date the key was last validated, shown by
|
|
77
|
+
`vocalize auth status`. An older item is replaced in place on the next
|
|
78
|
+
`vocalize auth login`.
|
|
79
|
+
|
|
80
|
+
### Added
|
|
81
|
+
|
|
82
|
+
- **An Anthropic key slot, and a Keys tab that can test and remove.**
|
|
83
|
+
`vocalize auth login --provider anthropic` stores the key the `anthropic`
|
|
84
|
+
cleanup backend uses, under its own keychain item; `auth status`,
|
|
85
|
+
`auth logout` and `vocalize usage` know the slot too. It is a key, not a
|
|
86
|
+
voice: the chain does not accept it. On the portal's Keys tab every slot
|
|
87
|
+
(the three voice providers and Anthropic) gets **Test without storing**,
|
|
88
|
+
which checks a key and keeps nothing, and **Remove stored key**, which
|
|
89
|
+
reads the keychain back before it says the key is gone; each card shows
|
|
90
|
+
when its key was last checked. The key field is `autocomplete=
|
|
91
|
+
"new-password"`, the value Safari and Chrome honour on a password field.
|
|
92
|
+
The Local tab gained a select for the cleanup backend.
|
|
93
|
+
|
|
94
|
+
- **An Anthropic API backend for the cleanup pass** (`[stt] cleanup =
|
|
95
|
+
"anthropic"`): one Messages API call with a key from `ANTHROPIC_API_KEY`
|
|
96
|
+
or the keychain slot `anthropic-api-key`, under a monthly character
|
|
97
|
+
budget (`[providers.anthropic] monthly_chars`, 2,000,000 when unset)
|
|
98
|
+
counted in the usage ledger. The key is stored from the CLI or the
|
|
99
|
+
portal's Keys tab, as described above.
|
|
100
|
+
- **A `[notes]` config table** (`folder`, `template`, `summarizer`,
|
|
101
|
+
`keep_audio`, `model`) is parsed, validated and preserved by every writer
|
|
102
|
+
of the config file. `vocalize notes` itself arrives in 0.14.0.
|
|
103
|
+
- **`large-v3-turbo-q8_0`** joins the dictation models: the same turbo model
|
|
104
|
+
as `q5_0` with 8-bit weights (834 MB on disk), for machines with 16 GB or
|
|
105
|
+
more. `vocalize local install --stt --model large-v3-turbo-q8_0`. Pinned
|
|
106
|
+
from a completed download like the other three.
|
|
107
|
+
|
|
108
|
+
## 0.11.0 - 2026-09-05
|
|
109
|
+
|
|
110
|
+
### Added
|
|
111
|
+
|
|
112
|
+
- **A web portal page** (`vocalize portal`) for the whole config surface: reorder
|
|
113
|
+
the provider chain; set each provider's voice, speed and monthly budget with a
|
|
114
|
+
live preview; store and check API keys (masked, `autocomplete="off"`); watch
|
|
115
|
+
usage; and drive the local Kokoro and whisper installs with progress — no more
|
|
116
|
+
hand-editing `config.toml`. Five tabs (Chain, Providers, Keys, Usage, Local)
|
|
117
|
+
plus a persistent readiness sidebar reading `/api/state`, with buttons on the
|
|
118
|
+
rows that map to a vocalize command the portal already runs. Every save goes
|
|
119
|
+
through the same compare-and-swap the CLI uses, so a change made elsewhere
|
|
120
|
+
while the page was open is refused rather than silently overwritten. Ships as
|
|
121
|
+
static HTML and JS with no framework, no inline script and no external
|
|
122
|
+
resource — the whole surface has to work with the network cable pulled — over
|
|
123
|
+
a loopback-only, one-time-code session (DEC-004).
|
|
124
|
+
- **A voice picker on the Providers tab.** `GET /api/voices/<name>` returns the
|
|
125
|
+
provider's own `list_voices()`, paginated and cached for the life of the
|
|
126
|
+
portal process, so the field is a real dropdown of the account's actual
|
|
127
|
+
voices with the current value pre-selected, and `"Type a voice id…"` for an
|
|
128
|
+
id the list doesn't carry — rather than a hand-typed guess.
|
|
129
|
+
|
|
130
|
+
### Fixed
|
|
131
|
+
|
|
132
|
+
- **`vocalize voices` and `vocalize usage` printed the ElevenLabs API key to
|
|
133
|
+
stderr when the API quoted it back.** The SDK's `ApiError` renders the whole
|
|
134
|
+
response body and header dict, and both commands printed that text verbatim —
|
|
135
|
+
so a key the API named in an error reached terminal scrollback, any session
|
|
136
|
+
log, and whatever the user pasted into a bug report. `auth.scrub` already
|
|
137
|
+
existed for exactly this, but three callers never reached it. The scrub now
|
|
138
|
+
happens inside `tts` itself, below every caller, against the key the client
|
|
139
|
+
was built with. Found by the 0.11.0 release review (DEC-021).
|
|
140
|
+
- **The portal's `say` voice list failed on the first ask.** The route gave every
|
|
141
|
+
provider the two-second budget meant for network probes, and enumerating the
|
|
142
|
+
system voices takes longer than that on a current Mac — so the one provider
|
|
143
|
+
needing no key, no account and no network was the one that said "couldn't
|
|
144
|
+
fetch the list". Offline lists get their own budget now.
|
|
145
|
+
- **A key of four characters or fewer was shown in full** by `vocalize auth
|
|
146
|
+
status` and on the portal's Keys tab — a truncated paste, or another tool's
|
|
147
|
+
short secret in the same environment variable, reproduced whole in output the
|
|
148
|
+
design treats as safe to screenshot. Anything under eight characters now masks
|
|
149
|
+
to `…` alone.
|
|
150
|
+
- **The portal's Keys tab pointed at Keychain Access** to remove a stored key.
|
|
151
|
+
It now names `vocalize auth logout --provider <name>`, which reads the entry
|
|
152
|
+
back and refuses to claim a removal it cannot verify.
|
|
153
|
+
- **The ElevenLabs SDK client followed a redirect and re-sent the API key
|
|
154
|
+
wherever it pointed, cross-origin, over plain http.** The SDK's httpx client
|
|
155
|
+
follows a 3xx by default; every ElevenLabs endpoint is a fixed URL, so a
|
|
156
|
+
redirect was never legitimate and the key had no business leaving the
|
|
157
|
+
original origin. Present since 0.9.0, when the ElevenLabs provider first
|
|
158
|
+
shipped. Closed by building the client with `follow_redirects=False` — the
|
|
159
|
+
same rule the urllib-based providers already held via `_http._NoRedirects` —
|
|
160
|
+
and proven on two real loopback HTTP servers: the second origin never sees
|
|
161
|
+
the request.
|
|
162
|
+
- **Nothing could rewrite a config file containing an `[stt]` table** —
|
|
163
|
+
which is every config a dictation user has had since 0.10.0 added the
|
|
164
|
+
table. The serialiser wrote flat keys and `[providers.*]` and had no
|
|
165
|
+
case for any other table, so `stt` reached the scalar renderer as a
|
|
166
|
+
`dict` and raised: "The config file has a table under 'stt'. The wizard
|
|
167
|
+
only manages flat keys, so it will not rewrite this file — edit that
|
|
168
|
+
file by hand." That refusal came out of `vocalize wizard` before it
|
|
169
|
+
asked its first question, and out of `vocalize chain google say` instead
|
|
170
|
+
of a write. The only way out was to delete the `[stt]` table, run the
|
|
171
|
+
command, and put it back. `[stt]` is now rendered as its own table, so
|
|
172
|
+
both commands rewrite the file and leave the dictation settings alone.
|
|
173
|
+
The rewritten file puts `[stt]` ahead of `[providers.*]` whatever order
|
|
174
|
+
they were in before — identical TOML, a one-off cosmetic diff.
|
|
175
|
+
- **Two writers saving at the same moment could lose one of the two
|
|
176
|
+
changes.** The config write compared the file against the fingerprint it
|
|
177
|
+
had read and then renamed a temp file over it, with the two steps not
|
|
178
|
+
held together, so two saves that overlapped could both see an unchanged
|
|
179
|
+
file, both write, and both report success while only the second survived.
|
|
180
|
+
The compare and the rename now happen under one lock. Two `vocalize`
|
|
181
|
+
*processes* saving in the same instant can still race — the fingerprint
|
|
182
|
+
check narrows that to a window of microseconds, and closing it entirely
|
|
183
|
+
needs a lock file, which is deliberately not what this is.
|
|
184
|
+
- **A config save rendered through a fixed `config.toml.tmp`**, so two
|
|
185
|
+
writers at once could render through the same temp file and leave one of
|
|
186
|
+
them writing a file the other had already renamed away — reported as
|
|
187
|
+
"Could not write config file", or worse, reported as success for bytes
|
|
188
|
+
that were not in the file. Each writer now gets its own temp file, made
|
|
189
|
+
with `mkstemp` in the same directory, still `0600`, and still renamed
|
|
190
|
+
into place atomically. The predictable name a local process could
|
|
191
|
+
pre-plant as a symlink is gone with it.
|
|
192
|
+
|
|
193
|
+
### Known limitation
|
|
194
|
+
|
|
195
|
+
- **Any other process on the Mac can close the portal with five wrong or
|
|
196
|
+
missing-origin requests to `/api/session`**, no code and no token needed —
|
|
197
|
+
the same guard that stops a cross-origin web page from doing it has no way
|
|
198
|
+
to tell that traffic apart from a local script's. This is availability
|
|
199
|
+
only: nothing is read or changed, the portal just exits. Rerun
|
|
200
|
+
`vocalize portal` and carry on. Accepted rather than fixed, since anything
|
|
201
|
+
able to send that traffic locally could kill the process outright anyway
|
|
202
|
+
(DEC-018).
|
|
203
|
+
|
|
6
204
|
## 0.10.2 - 2026-09-02
|
|
7
205
|
|
|
8
206
|
### Added
|
|
@@ -1,11 +1,48 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: vocalize-cli
|
|
3
|
+
Version: 0.12.0
|
|
4
|
+
Summary: A local-first CLI that turns text, markdown, or piped stdin into speech: the on-device Kokoro voice by default, cloud voices optional, with markdown-table-aware preprocessing and Claude Code hooks.
|
|
5
|
+
Project-URL: Homepage, https://github.com/matthager12-collab/vocalize
|
|
6
|
+
Project-URL: Repository, https://github.com/matthager12-collab/vocalize
|
|
7
|
+
Author: Mat
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Keywords: claude-code,cli,elevenlabs,kokoro,local,markdown,text-to-speech
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Requires-Dist: click>=8.1
|
|
22
|
+
Requires-Dist: elevenlabs>=2.0
|
|
23
|
+
Requires-Dist: keyring>=25
|
|
24
|
+
Requires-Dist: tomli>=2.0; python_version < '3.11'
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: build; extra == 'dev'
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.0; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
30
|
+
Requires-Dist: twine; extra == 'dev'
|
|
31
|
+
Provides-Extra: dotenv
|
|
32
|
+
Requires-Dist: python-dotenv>=1.0; extra == 'dotenv'
|
|
33
|
+
Provides-Extra: polly
|
|
34
|
+
Requires-Dist: boto3>=1.34; extra == 'polly'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
1
37
|
# vocalize
|
|
2
38
|
|
|
3
39
|
[](https://github.com/matthager12-collab/vocalize/actions/workflows/ci.yml)
|
|
4
40
|
|
|
5
41
|
A command-line tool that turns text, markdown files, or piped stdin into
|
|
6
|
-
natural-sounding speech
|
|
7
|
-
plus a hook that wires it directly into
|
|
8
|
-
so Claude's responses get read
|
|
42
|
+
natural-sounding speech on your own machine by default, with cloud voices as
|
|
43
|
+
an option — plus a hook that wires it directly into
|
|
44
|
+
[Claude Code](https://claude.com/claude-code), so Claude's responses get read
|
|
45
|
+
aloud in your terminal or IDE.
|
|
9
46
|
|
|
10
47
|
## Quickstart
|
|
11
48
|
|
|
@@ -14,17 +51,19 @@ pipx install vocalize-cli
|
|
|
14
51
|
```
|
|
15
52
|
|
|
16
53
|
```bash
|
|
17
|
-
vocalize
|
|
54
|
+
vocalize local install
|
|
18
55
|
```
|
|
19
56
|
|
|
20
|
-
|
|
57
|
+
Downloads the on-device Kokoro voice once (about 350 MB; needs
|
|
58
|
+
[`uv`](https://docs.astral.sh/uv/)). Skip it and `vocalize` speaks through
|
|
59
|
+
macOS `say` until you come back to it — the fallback line tells you so.
|
|
21
60
|
|
|
22
61
|
```bash
|
|
23
62
|
vocalize speak "hello"
|
|
24
63
|
```
|
|
25
64
|
|
|
26
|
-
|
|
27
|
-
|
|
65
|
+
Prefer a cloud voice? See [Providers and fallback](#providers-and-fallback):
|
|
66
|
+
`vocalize config` walks you through a key, a voice and a speed.
|
|
28
67
|
|
|
29
68
|
## Why this exists
|
|
30
69
|
|
|
@@ -62,33 +101,8 @@ cd vocalize
|
|
|
62
101
|
pip install -e .
|
|
63
102
|
```
|
|
64
103
|
|
|
65
|
-
|
|
66
|
-
[
|
|
67
|
-
(free tier: 10,000 characters/month, API access included, no commercial
|
|
68
|
-
license). Then, recommended, store it in your OS keychain:
|
|
69
|
-
|
|
70
|
-
```bash
|
|
71
|
-
vocalize auth login
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
This prompts for the key (input hidden), validates it against the
|
|
75
|
-
ElevenLabs API, and stores it via your OS's own keychain (macOS Keychain,
|
|
76
|
-
Windows Credential Locker, Linux Secret Service) — no plaintext file to
|
|
77
|
-
manage. Piping it in from a secret manager works too:
|
|
78
|
-
|
|
79
|
-
```bash
|
|
80
|
-
op read op://vault/elevenlabs/key | vocalize auth login --stdin
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
An environment variable or `.env` file work as well, and take priority over
|
|
84
|
-
the keychain if both are set:
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
export ELEVENLABS_API_KEY=your-key-here
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
or copy `.env.example` to `.env` and fill it in (requires the optional
|
|
91
|
-
`python-dotenv` extra: `pip install -e ".[dotenv]"`).
|
|
104
|
+
Cloud voices need an API key; storing one is covered under
|
|
105
|
+
[Providers and fallback](#providers-and-fallback).
|
|
92
106
|
|
|
93
107
|
## Usage
|
|
94
108
|
|
|
@@ -180,6 +194,9 @@ Hotkeys: `↑`/`↓` or `k`/`j` move, `Enter` selects, `p` previews the
|
|
|
180
194
|
highlighted voice, `m` types a value by hand, `q` or `Esc` cancels without
|
|
181
195
|
writing anything.
|
|
182
196
|
|
|
197
|
+
There's also a settings page in the browser — see
|
|
198
|
+
[Settings portal](#settings-portal) below.
|
|
199
|
+
|
|
183
200
|
| Setting | Flag | Env var | Config file key | Default |
|
|
184
201
|
|---|---|---|---|---|
|
|
185
202
|
| API key | `--api-key` | `ELEVENLABS_API_KEY` | not read from the config file | stored via `vocalize auth` |
|
|
@@ -203,7 +220,8 @@ back to `~/.config/vocalize/config.toml`. Flat keys, no sections:
|
|
|
203
220
|
```toml
|
|
204
221
|
chain = ["elevenlabs", "google", "say"]
|
|
205
222
|
|
|
206
|
-
# Flat keys
|
|
223
|
+
# Flat keys are the original cloud voice's settings, unchanged since
|
|
224
|
+
# before there was a chain; see Providers and fallback.
|
|
207
225
|
voice = "21m00Tcm4TlvDq8ikWAM"
|
|
208
226
|
model = "eleven_flash_v2_5"
|
|
209
227
|
speed = 0.95
|
|
@@ -259,6 +277,43 @@ configures a TTS provider — see
|
|
|
259
277
|
[Dictation](#dictation-speech-to-text) below for every key and its
|
|
260
278
|
allowlist.
|
|
261
279
|
|
|
280
|
+
## Settings portal
|
|
281
|
+
|
|
282
|
+
A page in your own browser for the whole config surface above, so you don't
|
|
283
|
+
have to hand-edit `config.toml` or click through `vocalize config` one
|
|
284
|
+
setting at a time.
|
|
285
|
+
|
|
286
|
+
```bash
|
|
287
|
+
vocalize portal # opens your browser at a one-time link
|
|
288
|
+
vocalize portal --no-browser # prints the link instead (headless, SSH)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
Five tabs:
|
|
292
|
+
|
|
293
|
+
- **Chain** — reorder providers, or add and remove one.
|
|
294
|
+
- **Providers** — each provider's voice, model, speed, and monthly budget,
|
|
295
|
+
with a live preview of the voice you're looking at.
|
|
296
|
+
- **Keys** — store, test or remove an API key for each provider and for
|
|
297
|
+
Anthropic (the dictation cleanup backend, not a voice); the field is
|
|
298
|
+
masked and never offers to autocomplete, a stored key is never shown
|
|
299
|
+
again, and each card says when the key was last checked.
|
|
300
|
+
- **Usage** — this month's spend and quota per provider.
|
|
301
|
+
- **Local** — install or update the on-device Kokoro and whisper models,
|
|
302
|
+
with progress.
|
|
303
|
+
|
|
304
|
+
The opening link works once and for 60 seconds; after that, run the command
|
|
305
|
+
again for a new one. The server only ever listens on `127.0.0.1`, and it
|
|
306
|
+
closes itself on Ctrl-C or after fifteen minutes with nothing to do. Nothing
|
|
307
|
+
you change on the page reaches disk until you press Save on that tab — and
|
|
308
|
+
a save made against a config file that's changed on disk since the page
|
|
309
|
+
loaded is refused rather than silently overwritten, the same protection
|
|
310
|
+
`vocalize chain` and `vocalize config` already have.
|
|
311
|
+
|
|
312
|
+
It assumes a single-user machine: everything that reads or changes your
|
|
313
|
+
settings is behind that one-time token, but any other process on the Mac
|
|
314
|
+
can still reach the port and close the portal under you. Nothing leaks if
|
|
315
|
+
that happens — it's a shutdown, not a read — just run the command again.
|
|
316
|
+
|
|
262
317
|
## Providers and fallback
|
|
263
318
|
|
|
264
319
|
vocalize tries providers in order — a **chain** — until one speaks. The
|
|
@@ -320,6 +375,41 @@ For the click-by-click setup of each provider — where to go, what to click,
|
|
|
320
375
|
the one command that stores the credential, the one command that proves it
|
|
321
376
|
works — see [docs/provider-credentials.md](docs/provider-credentials.md).
|
|
322
377
|
|
|
378
|
+
### Storing a cloud API key
|
|
379
|
+
|
|
380
|
+
For ElevenLabs, get a free API key at
|
|
381
|
+
[elevenlabs.io/app/settings/api-keys](https://elevenlabs.io/app/settings/api-keys)
|
|
382
|
+
(free tier: 10,000 characters/month, API access included, no commercial
|
|
383
|
+
license). Then, recommended, store it in your OS keychain:
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
vocalize auth login
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
This prompts for the key (input hidden), validates it against the
|
|
390
|
+
ElevenLabs API, and stores it via your OS's own keychain (macOS Keychain,
|
|
391
|
+
Windows Credential Locker, Linux Secret Service) — no plaintext file to
|
|
392
|
+
manage. On macOS the item is written and read through Apple's own
|
|
393
|
+
`security` tool, so the same key is readable from every Python and app
|
|
394
|
+
that runs vocalize — your terminal, Claude Code's shell, the portal —
|
|
395
|
+
with no keychain dialog, and `vocalize auth status` shows the date it was
|
|
396
|
+
last validated. Piping it in from a secret manager works too:
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
op read op://vault/elevenlabs/key | vocalize auth login --stdin
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
An environment variable or `.env` file work as well, and take priority over
|
|
403
|
+
the keychain if both are set:
|
|
404
|
+
|
|
405
|
+
```bash
|
|
406
|
+
export ELEVENLABS_API_KEY=your-key-here
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
or copy `.env.example` to `.env` and fill it in (requires the optional
|
|
410
|
+
`python-dotenv` extra: `pip install -e ".[dotenv]"`).
|
|
411
|
+
|
|
412
|
+
|
|
323
413
|
## Budgets and the usage ledger
|
|
324
414
|
|
|
325
415
|
Cloud providers don't stop at their free tier — they bill past it. vocalize
|
|
@@ -383,7 +473,8 @@ Use it for one read with `--provider kokoro`, or add it to your chain in
|
|
|
383
473
|
Long text streams: it's broken into ~400-character pieces, and playback
|
|
384
474
|
starts after the first one is ready — roughly 20–25 seconds of speech —
|
|
385
475
|
instead of waiting for the whole thing to render. Measured on this Mac
|
|
386
|
-
(M3): about 5x faster than real time, peaking around
|
|
476
|
+
(M3): about 5x faster than real time, peaking around 760 MB of RAM (measured
|
|
477
|
+
on an M4 on 2026-09-07; the M3 spike saw 870 MB) while
|
|
387
478
|
rendering. `vocalize stop`, run from any terminal, halts a Kokoro read
|
|
388
479
|
mid-sentence the same as any other provider.
|
|
389
480
|
|
|
@@ -405,7 +496,7 @@ vocalize local install --stt
|
|
|
405
496
|
A separate opt-in from Kokoro's `vocalize local install` — nothing here is
|
|
406
497
|
downloaded or built until you run this. It:
|
|
407
498
|
|
|
408
|
-
1. Downloads one whisper.cpp model (`
|
|
499
|
+
1. Downloads one whisper.cpp model (`large-v3-turbo-q5_0` by default, ~547 MB) from a
|
|
409
500
|
pinned Hugging Face revision, verified against a pinned sha256 before
|
|
410
501
|
it's kept.
|
|
411
502
|
2. Compiles and ad-hoc signs a small Swift recorder bundle, **Vocalize
|
|
@@ -479,10 +570,12 @@ overrides `[stt] max_seconds` for one invocation.
|
|
|
479
570
|
|
|
480
571
|
```toml
|
|
481
572
|
[stt]
|
|
482
|
-
model = "
|
|
483
|
-
language = "en" # a whisper.cpp language code
|
|
573
|
+
model = "large-v3-turbo-q5_0" # base.en | small.en | large-v3-turbo-q5_0 | large-v3-turbo-q8_0
|
|
574
|
+
language = "en" # a whisper.cpp language code ("auto" to detect)
|
|
484
575
|
input_device = "" # "" = system default; else an exact name from --list-devices
|
|
485
|
-
cleanup =
|
|
576
|
+
cleanup = "off" # off | local | claude-cli | anthropic — what tidies the transcript (never audio)
|
|
577
|
+
verbatim = false # true keeps every word even when cleanup is on
|
|
578
|
+
beam_size = 5 # 1-8 whisper.cpp beams; 1 is the greedy decoder
|
|
486
579
|
max_seconds = 120 # 1-600; the recorder self-stops here, dictate backstops it
|
|
487
580
|
sounds = true # the Tink/Pop/Glass feedback sounds
|
|
488
581
|
cues = "sounds" # "sounds" | "words" | "both" — speak "Start."/"Stopped."/"Ready." instead
|
|
@@ -490,10 +583,12 @@ cues = "sounds" # "sounds" | "words" | "both" — speak "Start."/"Stopped
|
|
|
490
583
|
|
|
491
584
|
| Key | Allowed values | Default |
|
|
492
585
|
|---|---|---|
|
|
493
|
-
| `model` | `base.en`, `small.en`, `large-v3-turbo-q5_0` | `
|
|
586
|
+
| `model` | `base.en`, `small.en`, `large-v3-turbo-q5_0`, `large-v3-turbo-q8_0` | `large-v3-turbo-q5_0` |
|
|
494
587
|
| `language` | a whisper.cpp language code (`en`, `es`, `fr`, …); an `.en` model must stay `en` | `en` |
|
|
495
588
|
| `input_device` | `""` (system default) or an exact name from `vocalize listen --list-devices`; ≤ 128 characters, printable, can't start with `-` | `""` |
|
|
496
|
-
| `cleanup` | `
|
|
589
|
+
| `cleanup` | `off`, `local` (0.13), `claude-cli`, `anthropic`; an old `true`/`false` reads as `claude-cli`/`off` | `off` |
|
|
590
|
+
| `verbatim` | `true` / `false` — keep every word even when cleanup is on | `false` |
|
|
591
|
+
| `beam_size` | integer, 1–8 | `5` |
|
|
497
592
|
| `paste` | reserved — not implemented in 0.10.0 | `false` |
|
|
498
593
|
| `max_seconds` | integer, 1–600 | `120` |
|
|
499
594
|
| `sounds` | `true` / `false` | `true` |
|
|
@@ -870,9 +965,11 @@ model.
|
|
|
870
965
|
re-approve it in System Settings › Privacy & Security › Microphone. An
|
|
871
966
|
install that doesn't change the source never re-signs, so this isn't
|
|
872
967
|
every upgrade — only ones that touch the recorder.
|
|
873
|
-
-
|
|
874
|
-
|
|
875
|
-
|
|
968
|
+
- **The smaller models mishear jargon.** `large-v3-turbo-q5_0` is the
|
|
969
|
+
default because it was the first model to keep "the merge" as two words
|
|
970
|
+
on the owner's own voice; `small.en` (465 MB) is the lighter choice for a
|
|
971
|
+
slow Mac but can mangle project-specific words (`pyproject`, a function
|
|
972
|
+
name) — stay on the default for accuracy, or turn on
|
|
876
973
|
`[stt] cleanup` so Claude fixes obvious transcription noise before it
|
|
877
974
|
reaches your clipboard (it still can't guess a word it never heard
|
|
878
975
|
correctly).
|