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.
Files changed (215) hide show
  1. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/CHANGELOG.md +198 -0
  2. vocalize_cli-0.10.2/README.md → vocalize_cli-0.12.0/PKG-INFO +142 -45
  3. vocalize_cli-0.10.2/PKG-INFO → vocalize_cli-0.12.0/README.md +106 -81
  4. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/dictation.md +28 -14
  5. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/installation.md +14 -1
  6. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/choreography.md +110 -0
  7. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/decisions.md +509 -0
  8. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/design.md +355 -0
  9. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/plan.md +331 -0
  10. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/review-0.12.0.md +142 -0
  11. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/review-plan-2026-09-06.md +41 -0
  12. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/project-plan.md +76 -0
  13. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/report.md +16 -0
  14. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/task-report.md +30 -0
  15. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/validate-exit.sh +134 -0
  16. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-10-release-0-13-0/project-plan.md +50 -0
  17. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-10-release-0-13-0/validate-exit.sh +124 -0
  18. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-11-cue-hold-paste/project-plan.md +52 -0
  19. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-11-cue-hold-paste/validate-exit.sh +125 -0
  20. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-11b-playback-pause/project-plan.md +54 -0
  21. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-11b-playback-pause/validate-exit.sh +128 -0
  22. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-12-release-0-13-1/project-plan.md +47 -0
  23. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-12-release-0-13-1/validate-exit.sh +122 -0
  24. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-13-spikes/project-plan.md +45 -0
  25. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-13-spikes/validate-exit.sh +114 -0
  26. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-14-local-llm/project-plan.md +50 -0
  27. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-14-local-llm/validate-exit.sh +124 -0
  28. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-15-notes/project-plan.md +48 -0
  29. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-15-notes/validate-exit.sh +111 -0
  30. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-15b-recording-pause/project-plan.md +57 -0
  31. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-15b-recording-pause/validate-exit.sh +131 -0
  32. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-16-release-0-14-0/project-plan.md +50 -0
  33. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-16-release-0-14-0/validate-exit.sh +124 -0
  34. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-17-optional-spikes/project-plan.md +44 -0
  35. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-17-optional-spikes/validate-exit.sh +118 -0
  36. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/project-plan.md +49 -0
  37. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/report.md +16 -0
  38. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/task-report.md +35 -0
  39. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/validate-exit.sh +113 -0
  40. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/project-plan.md +55 -0
  41. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/report.md +20 -0
  42. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/task-report.md +36 -0
  43. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/validate-exit.sh +152 -0
  44. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-4-keychain/project-plan.md +80 -0
  45. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-4-keychain/report.md +16 -0
  46. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-4-keychain/task-report.md +34 -0
  47. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-4-keychain/validate-exit.sh +134 -0
  48. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-5-keys-tab/project-plan.md +47 -0
  49. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-5-keys-tab/report.md +24 -0
  50. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-5-keys-tab/task-report.md +31 -0
  51. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-5-keys-tab/validate-exit.sh +118 -0
  52. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-6-release-0-12-0/project-plan.md +50 -0
  53. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-6-release-0-12-0/validate-exit.sh +124 -0
  54. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-7-hotkey-spike-and-builder/project-plan.md +45 -0
  55. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-7-hotkey-spike-and-builder/validate-exit.sh +113 -0
  56. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-8a-app-swift/project-plan.md +44 -0
  57. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-8a-app-swift/validate-exit.sh +115 -0
  58. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-8b-app-python/project-plan.md +49 -0
  59. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-8b-app-python/validate-exit.sh +119 -0
  60. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-9-doctor-integrate-setup/project-plan.md +48 -0
  61. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/run-9-doctor-integrate-setup/validate-exit.sh +121 -0
  62. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/spike-notes.md +38 -0
  63. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/split-assessment.md +57 -0
  64. vocalize_cli-0.12.0/docs/plans/2026-09-app-roadmap/verification.md +216 -0
  65. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/decisions.md +471 -1
  66. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/design.md +127 -1
  67. vocalize_cli-0.12.0/docs/plans/2026-09-next-features/review-0.11.0.md +134 -0
  68. vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-10-release-0-11-0/report.md +160 -0
  69. {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
  70. vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-7-portal-read/report.md +297 -0
  71. vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-7-portal-read/review-findings.md +130 -0
  72. {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
  73. vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-8-portal-write/report.md +346 -0
  74. {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
  75. vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-9-portal-page/report.md +231 -0
  76. vocalize_cli-0.12.0/docs/plans/2026-09-next-features/run-9-portal-page/review-findings.md +0 -0
  77. {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
  78. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/provider-credentials.md +72 -0
  79. vocalize_cli-0.12.0/docs/research/2026-09-04-app-roadmap-analysis.md +290 -0
  80. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/roadmap.md +4 -0
  81. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/speak_options.py +4 -1
  82. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/pyproject.toml +2 -2
  83. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/conftest.py +38 -0
  84. vocalize_cli-0.12.0/tests/portal_page_harness.js +814 -0
  85. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_auth.py +216 -1
  86. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_chain.py +39 -0
  87. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_cli.py +306 -5
  88. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_config.py +186 -7
  89. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_dictate.py +92 -114
  90. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_elevenlabs_provider.py +1 -1
  91. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_http.py +19 -0
  92. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_listen_check.py +3 -2
  93. vocalize_cli-0.12.0/tests/test_llm.py +491 -0
  94. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_local_install.py +4 -1
  95. vocalize_cli-0.12.0/tests/test_portal.py +4119 -0
  96. vocalize_cli-0.12.0/tests/test_portal_assets.py +405 -0
  97. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_readiness.py +49 -15
  98. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_speak_options.py +12 -0
  99. vocalize_cli-0.12.0/tests/test_tts.py +411 -0
  100. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_whisper_manifest.py +22 -5
  101. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_whisper_worker.py +73 -2
  102. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_wizard.py +399 -3
  103. vocalize_cli-0.12.0/uv.lock +1571 -0
  104. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/__init__.py +2 -2
  105. vocalize_cli-0.12.0/vocalize/assets/portal.html +295 -0
  106. vocalize_cli-0.12.0/vocalize/assets/portal.js +1595 -0
  107. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/auth.py +192 -9
  108. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/chain.py +21 -1
  109. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/cli.py +103 -31
  110. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/config.py +192 -17
  111. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/dictate.py +29 -77
  112. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/exceptions.py +15 -0
  113. vocalize_cli-0.12.0/vocalize/llm.py +315 -0
  114. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/install.py +10 -0
  115. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/whisper_manifest.py +11 -2
  116. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/whisper_worker.py +39 -2
  117. vocalize_cli-0.12.0/vocalize/portal.py +1805 -0
  118. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/_http.py +9 -0
  119. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/kokoro.py +2 -1
  120. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/readiness.py +32 -7
  121. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/tts.py +44 -5
  122. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/wizard.py +189 -17
  123. vocalize_cli-0.10.2/tests/test_tts.py +0 -201
  124. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/.env.example +0 -0
  125. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/.github/workflows/ci.yml +0 -0
  126. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/.gitignore +0 -0
  127. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/LICENSE +0 -0
  128. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/next-features-analysis.md +0 -0
  129. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/choreography.md +0 -0
  130. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/plan.md +0 -0
  131. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/review-0.10.0.md +0 -0
  132. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-1-status/project-plan.md +0 -0
  133. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-1-status/report.md +0 -0
  134. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-1-status/validate-exit.sh +0 -0
  135. {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
  136. {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
  137. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-2-stt-runtime/report.md +0 -0
  138. {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
  139. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-3-recorder/project-plan.md +0 -0
  140. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-3-recorder/report.md +0 -0
  141. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-3-recorder/validate-exit.sh +0 -0
  142. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-4-dictation/project-plan.md +0 -0
  143. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-4-dictation/report.md +0 -0
  144. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-4-dictation/validate-exit.sh +0 -0
  145. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-5-resume/project-plan.md +0 -0
  146. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-5-resume/report.md +0 -0
  147. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/run-5-resume/validate-exit.sh +0 -0
  148. {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
  149. {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
  150. {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
  151. {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
  152. {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
  153. {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
  154. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/spike-2026-09-01.md +0 -0
  155. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/split-assessment.md +0 -0
  156. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/plans/2026-09-next-features/verification.md +0 -0
  157. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/research/2026-09-01-config-portal-design.md +0 -0
  158. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/research/2026-09-01-dictation-design.md +0 -0
  159. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/docs/research/2026-09-01-voicebox-findings.md +0 -0
  160. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/claude_stop_hook.py +0 -0
  161. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/install_hook.py +0 -0
  162. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/install_quick_action.py +0 -0
  163. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Dictate with Vocalize.workflow/Contents/Info.plist +0 -0
  164. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Dictate with Vocalize.workflow/Contents/Resources/document.wflow +0 -0
  165. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Speak Latest Plan.workflow/Contents/Info.plist +0 -0
  166. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Speak Latest Plan.workflow/Contents/Resources/document.wflow +0 -0
  167. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Info.plist +0 -0
  168. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Speak with Vocalize.workflow/Contents/Resources/document.wflow +0 -0
  169. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Stop Vocalize.workflow/Contents/Info.plist +0 -0
  170. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/quick_actions/Stop Vocalize.workflow/Contents/Resources/document.wflow +0 -0
  171. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/hooks/speak_url_gate.py +0 -0
  172. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_audio.py +0 -0
  173. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_cache.py +0 -0
  174. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_claude_stop_hook.py +0 -0
  175. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_clipboard.py +0 -0
  176. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_cue_assets.py +0 -0
  177. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_exceptions.py +0 -0
  178. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_google_provider.py +0 -0
  179. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_install_hook.py +0 -0
  180. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_install_quick_action.py +0 -0
  181. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_kokoro_manifest.py +0 -0
  182. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_kokoro_provider.py +0 -0
  183. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_kokoro_worker.py +0 -0
  184. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_ledger.py +0 -0
  185. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_openai_provider.py +0 -0
  186. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_polly_provider.py +0 -0
  187. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_preprocess.py +0 -0
  188. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_providers_registry.py +0 -0
  189. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_recorder_build.py +0 -0
  190. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_say_provider.py +0 -0
  191. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_speak_url_gate.py +0 -0
  192. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/tests/test_uv_path.py +0 -0
  193. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/__main__.py +0 -0
  194. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/assets/cues/README.md +0 -0
  195. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/assets/cues/ready.wav +0 -0
  196. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/assets/cues/start.wav +0 -0
  197. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/assets/cues/stopped.wav +0 -0
  198. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/audio.py +0 -0
  199. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/cache.py +0 -0
  200. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/clipboard.py +0 -0
  201. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/interrupted.py +0 -0
  202. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/ledger.py +0 -0
  203. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/__init__.py +0 -0
  204. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/kokoro_manifest.py +0 -0
  205. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/local/kokoro_worker.py +0 -0
  206. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/preprocess.py +0 -0
  207. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/__init__.py +0 -0
  208. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/elevenlabs.py +0 -0
  209. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/google.py +0 -0
  210. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/openai.py +0 -0
  211. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/polly.py +0 -0
  212. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/providers/say.py +0 -0
  213. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/recorder/Info.plist.in +0 -0
  214. {vocalize_cli-0.10.2 → vocalize_cli-0.12.0}/vocalize/recorder/Recorder.entitlements +0 -0
  215. {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
  [![CI](https://github.com/matthager12-collab/vocalize/actions/workflows/ci.yml/badge.svg)](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 using the [ElevenLabs](https://elevenlabs.io) API —
7
- plus a hook that wires it directly into [Claude Code](https://claude.com/claude-code),
8
- so Claude's responses get read aloud automatically in your terminal or IDE.
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 config
54
+ vocalize local install
18
55
  ```
19
56
 
20
- Walks you through your API key, a voice, and a speed, and saves it all.
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
- To set up just the key, skip the wizard and run `vocalize auth login` — it
27
- stores the key in your OS keychain.
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
- Get a free ElevenLabs API key at
66
- [elevenlabs.io/app/settings/api-keys](https://elevenlabs.io/app/settings/api-keys)
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 = ElevenLabs, unchanged since before there was a chain.
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 870 MB of RAM while
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 (`small.en` by default, ~465 MB) from a
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 = "small.en" # base.en | small.en | large-v3-turbo-q5_0
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 = false # send the transcript (never audio) to Claude first
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` | `small.en` |
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` | `true` / `false` | `false` |
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
- - **`small.en` mishears jargon.** The default model does fine on ordinary
874
- speech but can mangle project-specific words (`pyproject`, a function
875
- name) — pick `large-v3-turbo-q5_0` for better accuracy, or turn on
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).