vocalize-cli 0.12.0__tar.gz → 0.13.1__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 (242) hide show
  1. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/CHANGELOG.md +100 -1
  2. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/PKG-INFO +71 -15
  3. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/README.md +70 -14
  4. vocalize_cli-0.13.1/docs/app.md +205 -0
  5. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/dictation.md +123 -29
  6. vocalize_cli-0.13.1/docs/installation.md +211 -0
  7. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/decisions.md +6 -6
  8. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/review-0.13.0.md +313 -0
  9. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-10-release-0-13-0/report.md +17 -0
  10. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-10-release-0-13-0/task-report.md +29 -0
  11. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-10-release-0-13-0/validate-exit.sh +2 -2
  12. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-11-cue-hold-paste/report.md +16 -0
  13. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-11b-playback-pause/report.md +14 -0
  14. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-11b-playback-pause/validate-exit.sh +1 -1
  15. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-12-release-0-13-1/report.md +10 -0
  16. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-12-release-0-13-1/validate-exit.sh +2 -2
  17. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-16-release-0-14-0/validate-exit.sh +2 -2
  18. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-6-release-0-12-0/report.md +31 -0
  19. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-6-release-0-12-0/validate-exit.sh +2 -2
  20. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-7-hotkey-spike-and-builder/report.md +17 -0
  21. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-7-hotkey-spike-and-builder/task-report.md +31 -0
  22. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-8a-app-swift/report.md +19 -0
  23. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-8a-app-swift/task-report.md +30 -0
  24. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-8a-app-swift/validate-exit.sh +12 -3
  25. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-8b-app-python/report.md +20 -0
  26. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-8b-app-python/task-report.md +36 -0
  27. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-9-doctor-integrate-setup/report.md +21 -0
  28. vocalize_cli-0.13.1/docs/plans/2026-09-app-roadmap/run-9-doctor-integrate-setup/task-report.md +29 -0
  29. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/spike-notes.md +18 -0
  30. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/verification.md +2 -2
  31. vocalize_cli-0.13.1/docs/research/2026-09-08-spoken-rendering-rules.md +119 -0
  32. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/roadmap.md +1 -1
  33. vocalize_cli-0.13.1/hooks/install_quick_action.py +13 -0
  34. vocalize_cli-0.13.1/hooks/speak_options.py +13 -0
  35. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/conftest.py +71 -0
  36. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/portal_page_harness.js +120 -1
  37. vocalize_cli-0.13.1/tests/test_app_build.py +242 -0
  38. vocalize_cli-0.13.1/tests/test_app_cli.py +777 -0
  39. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_cli.py +553 -2
  40. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_config.py +180 -0
  41. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_dictate.py +741 -50
  42. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_install_quick_action.py +70 -30
  43. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_local_install.py +27 -7
  44. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_portal.py +153 -0
  45. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_portal_assets.py +14 -2
  46. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_preprocess.py +118 -50
  47. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_readiness.py +391 -0
  48. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_recorder_build.py +1 -37
  49. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_speak_options.py +2 -1
  50. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_wizard.py +20 -0
  51. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/uv.lock +6 -6
  52. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/__init__.py +1 -1
  53. vocalize_cli-0.13.1/vocalize/app.py +406 -0
  54. vocalize_cli-0.13.1/vocalize/assets/claude/speak/SKILL.md +168 -0
  55. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/assets/portal.html +9 -0
  56. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/assets/portal.js +143 -16
  57. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize/assets}/quick_actions/Speak Latest Plan.workflow/Contents/Resources/document.wflow +1 -1
  58. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize/assets}/quick_actions/Speak with Vocalize.workflow/Contents/Resources/document.wflow +2 -2
  59. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/cli.py +339 -17
  60. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/config.py +297 -2
  61. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/dictate.py +322 -47
  62. vocalize_cli-0.13.1/vocalize/integrate.py +264 -0
  63. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/interrupted.py +34 -1
  64. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/local/install.py +172 -52
  65. vocalize_cli-0.13.1/vocalize/menubar/Info.plist.in +47 -0
  66. vocalize_cli-0.13.1/vocalize/menubar/VocalizeApp.swift +1560 -0
  67. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/portal.py +40 -3
  68. vocalize_cli-0.13.1/vocalize/preprocess.py +624 -0
  69. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/readiness.py +231 -2
  70. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/wizard.py +3 -1
  71. vocalize_cli-0.12.0/docs/installation.md +0 -483
  72. vocalize_cli-0.12.0/hooks/install_quick_action.py +0 -136
  73. vocalize_cli-0.12.0/vocalize/preprocess.py +0 -279
  74. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/.env.example +0 -0
  75. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/.github/workflows/ci.yml +0 -0
  76. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/.gitignore +0 -0
  77. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/LICENSE +0 -0
  78. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/next-features-analysis.md +0 -0
  79. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/choreography.md +0 -0
  80. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/design.md +0 -0
  81. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/plan.md +0 -0
  82. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/review-0.12.0.md +0 -0
  83. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/review-plan-2026-09-06.md +0 -0
  84. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/project-plan.md +0 -0
  85. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/report.md +0 -0
  86. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/task-report.md +0 -0
  87. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-1-local-first-defaults/validate-exit.sh +0 -0
  88. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-10-release-0-13-0/project-plan.md +0 -0
  89. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-11-cue-hold-paste/project-plan.md +0 -0
  90. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-11-cue-hold-paste/validate-exit.sh +0 -0
  91. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-11b-playback-pause/project-plan.md +0 -0
  92. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-12-release-0-13-1/project-plan.md +0 -0
  93. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-13-spikes/project-plan.md +0 -0
  94. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-13-spikes/validate-exit.sh +0 -0
  95. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-14-local-llm/project-plan.md +0 -0
  96. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-14-local-llm/validate-exit.sh +0 -0
  97. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-15-notes/project-plan.md +0 -0
  98. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-15-notes/validate-exit.sh +0 -0
  99. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-15b-recording-pause/project-plan.md +0 -0
  100. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-15b-recording-pause/validate-exit.sh +0 -0
  101. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-16-release-0-14-0/project-plan.md +0 -0
  102. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-17-optional-spikes/project-plan.md +0 -0
  103. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-17-optional-spikes/validate-exit.sh +0 -0
  104. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/project-plan.md +0 -0
  105. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/report.md +0 -0
  106. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/task-report.md +0 -0
  107. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-2-stt-decoding/validate-exit.sh +0 -0
  108. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/project-plan.md +0 -0
  109. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/report.md +0 -0
  110. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/task-report.md +0 -0
  111. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-3-llm-and-enums/validate-exit.sh +0 -0
  112. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-4-keychain/project-plan.md +0 -0
  113. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-4-keychain/report.md +0 -0
  114. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-4-keychain/task-report.md +0 -0
  115. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-4-keychain/validate-exit.sh +0 -0
  116. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-5-keys-tab/project-plan.md +0 -0
  117. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-5-keys-tab/report.md +0 -0
  118. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-5-keys-tab/task-report.md +0 -0
  119. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-5-keys-tab/validate-exit.sh +0 -0
  120. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-6-release-0-12-0/project-plan.md +0 -0
  121. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-7-hotkey-spike-and-builder/project-plan.md +0 -0
  122. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-7-hotkey-spike-and-builder/validate-exit.sh +0 -0
  123. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-8a-app-swift/project-plan.md +0 -0
  124. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-8b-app-python/project-plan.md +0 -0
  125. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-8b-app-python/validate-exit.sh +0 -0
  126. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-9-doctor-integrate-setup/project-plan.md +0 -0
  127. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/run-9-doctor-integrate-setup/validate-exit.sh +0 -0
  128. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-app-roadmap/split-assessment.md +0 -0
  129. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/choreography.md +0 -0
  130. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/decisions.md +0 -0
  131. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/design.md +0 -0
  132. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/plan.md +0 -0
  133. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/review-0.10.0.md +0 -0
  134. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/review-0.11.0.md +0 -0
  135. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-1-status/project-plan.md +0 -0
  136. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-1-status/report.md +0 -0
  137. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-1-status/validate-exit.sh +0 -0
  138. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-10-release-0-11-0/project-plan.md +0 -0
  139. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-10-release-0-11-0/report.md +0 -0
  140. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-10-release-0-11-0/validate-exit.sh +0 -0
  141. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-2-stt-runtime/project-plan.md +0 -0
  142. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-2-stt-runtime/report.md +0 -0
  143. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-2-stt-runtime/validate-exit.sh +0 -0
  144. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-3-recorder/project-plan.md +0 -0
  145. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-3-recorder/report.md +0 -0
  146. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-3-recorder/validate-exit.sh +0 -0
  147. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-4-dictation/project-plan.md +0 -0
  148. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-4-dictation/report.md +0 -0
  149. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-4-dictation/validate-exit.sh +0 -0
  150. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-5-resume/project-plan.md +0 -0
  151. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-5-resume/report.md +0 -0
  152. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-5-resume/validate-exit.sh +0 -0
  153. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-6-release-0-10-0/project-plan.md +0 -0
  154. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-6-release-0-10-0/report.md +0 -0
  155. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-6-release-0-10-0/validate-exit.sh +0 -0
  156. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-7-portal-read/project-plan.md +0 -0
  157. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-7-portal-read/report.md +0 -0
  158. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-7-portal-read/review-findings.md +0 -0
  159. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-7-portal-read/validate-exit.sh +0 -0
  160. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-8-portal-write/project-plan.md +0 -0
  161. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-8-portal-write/report.md +0 -0
  162. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-8-portal-write/validate-exit.sh +0 -0
  163. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-9-portal-page/project-plan.md +0 -0
  164. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-9-portal-page/report.md +0 -0
  165. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-9-portal-page/review-findings.md +0 -0
  166. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/run-9-portal-page/validate-exit.sh +0 -0
  167. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/spike-2026-09-01.md +0 -0
  168. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/split-assessment.md +0 -0
  169. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/plans/2026-09-next-features/verification.md +0 -0
  170. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/provider-credentials.md +0 -0
  171. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/research/2026-09-01-config-portal-design.md +0 -0
  172. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/research/2026-09-01-dictation-design.md +0 -0
  173. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/research/2026-09-01-voicebox-findings.md +0 -0
  174. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/docs/research/2026-09-04-app-roadmap-analysis.md +0 -0
  175. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/hooks/claude_stop_hook.py +0 -0
  176. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/hooks/install_hook.py +0 -0
  177. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/hooks/speak_url_gate.py +0 -0
  178. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/pyproject.toml +0 -0
  179. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_audio.py +0 -0
  180. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_auth.py +0 -0
  181. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_cache.py +0 -0
  182. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_chain.py +0 -0
  183. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_claude_stop_hook.py +0 -0
  184. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_clipboard.py +0 -0
  185. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_cue_assets.py +0 -0
  186. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_elevenlabs_provider.py +0 -0
  187. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_exceptions.py +0 -0
  188. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_google_provider.py +0 -0
  189. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_http.py +0 -0
  190. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_install_hook.py +0 -0
  191. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_kokoro_manifest.py +0 -0
  192. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_kokoro_provider.py +0 -0
  193. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_kokoro_worker.py +0 -0
  194. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_ledger.py +0 -0
  195. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_listen_check.py +0 -0
  196. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_llm.py +0 -0
  197. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_openai_provider.py +0 -0
  198. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_polly_provider.py +0 -0
  199. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_providers_registry.py +0 -0
  200. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_say_provider.py +0 -0
  201. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_speak_url_gate.py +0 -0
  202. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_tts.py +0 -0
  203. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_uv_path.py +0 -0
  204. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_whisper_manifest.py +0 -0
  205. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/tests/test_whisper_worker.py +0 -0
  206. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/__main__.py +0 -0
  207. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/assets/cues/README.md +0 -0
  208. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/assets/cues/ready.wav +0 -0
  209. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/assets/cues/start.wav +0 -0
  210. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/assets/cues/stopped.wav +0 -0
  211. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize/assets}/quick_actions/Dictate with Vocalize.workflow/Contents/Info.plist +0 -0
  212. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize/assets}/quick_actions/Dictate with Vocalize.workflow/Contents/Resources/document.wflow +0 -0
  213. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize/assets}/quick_actions/Speak Latest Plan.workflow/Contents/Info.plist +0 -0
  214. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize/assets}/quick_actions/Speak with Vocalize.workflow/Contents/Info.plist +0 -0
  215. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize/assets}/quick_actions/Stop Vocalize.workflow/Contents/Info.plist +0 -0
  216. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize/assets}/quick_actions/Stop Vocalize.workflow/Contents/Resources/document.wflow +0 -0
  217. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/audio.py +0 -0
  218. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/auth.py +0 -0
  219. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/cache.py +0 -0
  220. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/chain.py +0 -0
  221. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/clipboard.py +0 -0
  222. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/exceptions.py +0 -0
  223. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/ledger.py +0 -0
  224. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/llm.py +0 -0
  225. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/local/__init__.py +0 -0
  226. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/local/kokoro_manifest.py +0 -0
  227. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/local/kokoro_worker.py +0 -0
  228. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/local/whisper_manifest.py +0 -0
  229. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/local/whisper_worker.py +0 -0
  230. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/providers/__init__.py +0 -0
  231. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/providers/_http.py +0 -0
  232. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/providers/elevenlabs.py +0 -0
  233. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/providers/google.py +0 -0
  234. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/providers/kokoro.py +0 -0
  235. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/providers/openai.py +0 -0
  236. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/providers/polly.py +0 -0
  237. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/providers/say.py +0 -0
  238. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/recorder/Info.plist.in +0 -0
  239. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/recorder/Recorder.entitlements +0 -0
  240. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/recorder/VocalizeRecorder.swift +0 -0
  241. {vocalize_cli-0.12.0/hooks → vocalize_cli-0.13.1/vocalize}/speak_options.py +0 -0
  242. {vocalize_cli-0.12.0 → vocalize_cli-0.13.1}/vocalize/tts.py +0 -0
@@ -5,7 +5,106 @@ All notable changes to this project are documented here. Format follows
5
5
 
6
6
  ## Unreleased
7
7
 
8
- Nothing yet.
8
+ ### Added
9
+
10
+ - **`vocalize pause`** pauses live playback and saves your place for up to an
11
+ hour under `~/.cache/vocalize/interrupted.*` (0600 mode), continuing where you
12
+ left off with `vocalize resume` (with a 1.0-second rewind overlap).
13
+ - **`[app] stop_hotkey = "stop" | "pause"`** (default `"stop"`). Set to
14
+ `"pause"`, the stop chord (Control-Option-Command-X) becomes a play/pause
15
+ toggle with zero Accessibility re-grant: pauses live audio, and resumes a
16
+ saved read when nothing is playing (refusing silently during dictation).
17
+ - `vocalize settings` prints `app.stop_hotkey=…`.
18
+
19
+ - **The dictation cue no longer reaches the transcript.** The "Start."
20
+ word and/or Tink now play after the microphone is actually open, timed
21
+ off the real first growth of the recording rather than a guess, and
22
+ those seconds are trimmed back off the head of the take before it's
23
+ transcribed ([#2](https://github.com/matthager12-collab/vocalize/issues/2)).
24
+ A machine where the recording never grows incrementally falls back to
25
+ today's order (word before the microphone opens) with no trim.
26
+ - **Hold-to-talk.** `[app] dictate_mode = "hold"` turns the dictate chord
27
+ from a toggle into a press-and-hold: hold it, speak, let go, no cancel
28
+ window. `vocalize dictate --start` / `--stop` drive the same thing from
29
+ a terminal or a script. `--start` is idempotent; `--stop` never treats a
30
+ short hold as a cancel.
31
+ - **`[stt] paste`** (default off). When on, a dictation that copies to the
32
+ clipboard also pastes into the app you started it in — a marker file
33
+ carrying the dictation's nonce is how the menu-bar app knows it's safe
34
+ to paste (same session, same frontmost app, under 2 seconds old); a
35
+ window switch leaves the transcript on the clipboard and says "Copied,
36
+ not pasted (window changed)." instead.
37
+ - `vocalize settings` prints `stt.paste=…`.
38
+
39
+ ## 0.13.0 - 2026-09-08
40
+
41
+ ### Added
42
+
43
+ - **The `[app]` config table** for the menu-bar app that arrives in 0.13.0:
44
+ `dictate`, `speak` and `stop` chords (`ctrl+alt+cmd+d`, `+s`, `+x` by
45
+ default; tokens joined by `+`, modifiers ctrl/alt/cmd/shift with their
46
+ long-form aliases, a key of a–z, 0–9 or f1–f12, ctrl or cmd required,
47
+ `""` disables) and `dictate_mode` (`toggle`; `hold` parses and resolves
48
+ to toggle with one warning until 0.13.1). The wizard renders it and
49
+ `vocalize settings` prints `app.<key>=…` in one canonical spelling.
50
+ - **`vocalize app install / uninstall / status [--json] / restart`** —
51
+ a menu-bar app (`Vocalize.app`) that owns the dictation, speak and stop
52
+ hotkeys system-wide, as a LaunchAgent, so a hotkey works from any app
53
+ with no Services menu and no per-app shortcut assignment. `install`
54
+ builds the bundle, warns about a running Hammerspoon or a Services
55
+ shortcut assigned to the "Dictate with Vocalize" Quick Action, resets
56
+ the Accessibility grant
57
+ only on a rebuild, and loads the LaunchAgent; `status` reports what's
58
+ built, whether the agent is loaded, and the app's own hotkey and
59
+ Accessibility state; `restart` kicks the running app without touching
60
+ the LaunchAgent; `uninstall` removes everything `install` put down
61
+ and leaves the Accessibility entry in System Settings alone. See
62
+ [docs/app.md](docs/app.md).
63
+ - **`dictate.session` gains a `state` and a per-dictation `nonce`.**
64
+ `state` moves through `starting` → `recording` → `transcribing` as a
65
+ dictation runs, so anything reading the session file (the app included)
66
+ knows which stage it's watching, not just that one is in progress.
67
+ `nonce` ties one dictation's session to its eventual `dictate.copied`
68
+ write. Unknown keys in the file are still ignored.
69
+ - **`vocalize doctor [--json]`** — every readiness row plus the toolchain
70
+ and environment checks `status` doesn't cover: `cli path`, `uv`,
71
+ `swiftc`, `claude` on PATH, the running console script's shebang
72
+ (brew rot), a Hammerspoon or old Quick Action conflict, `vocalize
73
+ --version` cold-start time, app bundle state with its repair command,
74
+ and the notes folder's size on disk. Exits 1 only on a failing row;
75
+ warn rows print but don't fail a script.
76
+ - **`vocalize integrate claude [--yes]`** — one command in place of the
77
+ hand-rolled `/speak` skill and the standalone Quick Action installer.
78
+ Checks `vocalize`, `claude`, `node`, and `python3` on PATH, installs
79
+ `~/.claude/skills/speak/SKILL.md` from the package (kept unless
80
+ `--yes`), installs the four Quick Actions into `~/Library/Services`,
81
+ and prints the GUI-only hotkey and Accessibility steps. The Quick
82
+ Action installer and its `.workflow` bundles moved into the package
83
+ (`vocalize/assets/quick_actions/`); `hooks/install_quick_action.py` is
84
+ now a thin wrapper around it. The baked `claude` path is the stable
85
+ symlink, not `.resolve()`'s version-pinned Caskroom target, so a
86
+ `brew upgrade claude-code` no longer silently breaks the summary
87
+ picker.
88
+ - **The Setup tab** in `vocalize portal` — a nine-step checklist
89
+ (toolchain, both bundles, login item, chords, model download,
90
+ microphone, `/speak` and Quick Actions, Accessibility, self-test)
91
+ built from the readiness rows the sidebar shows; its first step points at
92
+ `vocalize doctor`. Its install
93
+ buttons drive the existing `/api/local/install` routes, now including
94
+ target `app`. See [docs/app.md](docs/app.md#the-setup-tab).
95
+
96
+ ### Changed
97
+
98
+ - **The recorder is built by a general bundle builder.** `build_bundle(BundleSpec)`
99
+ replaces the recorder-only code path; `build_recorder` is the recorder's
100
+ door onto it and its build is unchanged byte for byte (a golden test pins
101
+ the `swiftc` and `codesign` argv, because any drift is a new ad-hoc
102
+ signature and a microphone re-grant). `vocalize local uninstall --stt`
103
+ now removes only the recorder bundle and its stamp, never the whole
104
+ `~/.cache/vocalize/bin`.
105
+ - **`vocalize clip` exits 3, not 1, when it refuses a credential-shaped
106
+ clipboard.** A script that already treats exit 1 as "something went
107
+ wrong, try again" can now tell that refusal apart from a real failure.
9
108
 
10
109
  ## 0.12.0 - 2026-09-07
11
110
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: vocalize-cli
3
- Version: 0.12.0
3
+ Version: 0.13.1
4
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
5
  Project-URL: Homepage, https://github.com/matthager12-collab/vocalize
6
6
  Project-URL: Repository, https://github.com/matthager12-collab/vocalize
@@ -513,13 +513,24 @@ more accurate) or `--model base.en` (~141 MB, fastest, least accurate).
513
513
  ### The hotkey
514
514
 
515
515
  ```bash
516
- python3 hooks/install_quick_action.py
516
+ vocalize app install
517
+ ```
518
+
519
+ installs and starts **Vocalize.app**, a menu-bar app that owns the
520
+ dictation hotkey (and speak/stop) system-wide, no Services menu or
521
+ per-app shortcut assignment needed — see [docs/app.md](docs/app.md) for
522
+ install, uninstall, status and restart. `vocalize dictate` is the same
523
+ command from a terminal, if you'd rather trigger it that way.
524
+
525
+ Prefer a Quick Action instead? That still works:
526
+
527
+ ```bash
528
+ vocalize integrate claude
517
529
  ```
518
530
 
519
531
  then assign a shortcut under **System Settings › Keyboard › Keyboard
520
532
  Shortcuts › Services › Text › "Dictate with Vocalize"** — ⌃⌥⌘D is free by
521
- default and a sensible pick. `vocalize dictate` is the same command from a
522
- terminal, if you'd rather trigger it that way.
533
+ default and a sensible pick.
523
534
 
524
535
  ### How a dictation works
525
536
 
@@ -589,7 +600,7 @@ cues = "sounds" # "sounds" | "words" | "both" — speak "Start."/"Stopped
589
600
  | `cleanup` | `off`, `local` (0.13), `claude-cli`, `anthropic`; an old `true`/`false` reads as `claude-cli`/`off` | `off` |
590
601
  | `verbatim` | `true` / `false` — keep every word even when cleanup is on | `false` |
591
602
  | `beam_size` | integer, 1–8 | `5` |
592
- | `paste` | reserved — not implemented in 0.10.0 | `false` |
603
+ | `paste` | `true` / `false` — paste into the app you dictated in, after copying (see docs/dictation.md § Auto-paste) | `false` |
593
604
  | `max_seconds` | integer, 1–600 | `120` |
594
605
  | `sounds` | `true` / `false` | `true` |
595
606
  | `cues` | `sounds`, `words`, `both` | `sounds` |
@@ -609,6 +620,37 @@ set it:
609
620
  input_device = "MacBook Pro Microphone"
610
621
  ```
611
622
 
623
+ ### Configuration: the `[app]` table
624
+
625
+ The chords the menu-bar app owns, system-wide. A change needs
626
+ `vocalize app restart` before the app picks it up.
627
+
628
+ ```toml
629
+ [app]
630
+ dictate = "ctrl+alt+cmd+d" # start/stop dictation
631
+ dictate_mode = "toggle" # toggle | hold
632
+ speak = "ctrl+alt+cmd+s" # speak the selection
633
+ stop = "ctrl+alt+cmd+x" # stop whatever is playing
634
+ ```
635
+
636
+ | Key | Allowed values | Default |
637
+ |---|---|---|
638
+ | `dictate` | a chord, or `""` to disable | `ctrl+alt+cmd+d` |
639
+ | `dictate_mode` | `toggle`, `hold` | `toggle` |
640
+ | `speak` | a chord, or `""` to disable | `ctrl+alt+cmd+s` |
641
+ | `stop` | a chord, or `""` to disable | `ctrl+alt+cmd+x` |
642
+
643
+ **Chord grammar:** tokens joined by `+`. Modifiers are `ctrl`, `alt`,
644
+ `cmd`, `shift` (long spellings `control`, `option`, `command` also work);
645
+ the key is one of `a`-`z`, `0`-`9`, `f1`-`f12`. A chord must include
646
+ `ctrl` or `cmd` — macOS refuses to register the rest. The three chords
647
+ must differ from each other, and `""` disables one.
648
+
649
+ A bad chord is a `ConfigError` naming the key, the value and the legal
650
+ keys — and, like any bad config value, it stops the command you ran, so
651
+ fix it before anything else works. `vocalize settings` prints the four
652
+ `app.*` values in their canonical spelling.
653
+
612
654
  ### `vocalize status`
613
655
 
614
656
  ```bash
@@ -631,6 +673,17 @@ get four permanent red rows for a feature nobody asked for):
631
673
  providers and dictation both — is `ok`, 1 otherwise, so it composes with
632
674
  `&&` in a script.
633
675
 
676
+ ### `vocalize doctor`
677
+
678
+ Everything `status` checks plus the machine itself: the CLI's own path and
679
+ start-up time, `uv`, `swiftc`, `claude`, the console script's interpreter,
680
+ Hammerspoon, the app bundle and the notes folder. Every row regardless of the
681
+ chain, exit 1 when any row fails. `--json` for scripts.
682
+
683
+ ```bash
684
+ vocalize doctor
685
+ ```
686
+
634
687
  ### Continuing an interrupted read
635
688
 
636
689
  Starting a dictation stops any read in progress, but doesn't throw the rest
@@ -640,6 +693,7 @@ where it stopped, and once your transcript has landed, vocalize asks:
640
693
  up after 15 seconds (counted as no). From a terminal, the same thing is:
641
694
 
642
695
  ```bash
696
+ vocalize pause # pause live playback and save your place
643
697
  vocalize resume # continue where the last read left off
644
698
  vocalize resume --forget # discard it instead
645
699
  ```
@@ -704,15 +758,16 @@ Four Services let you use vocalize from any app without a terminal:
704
758
  Install all four:
705
759
 
706
760
  ```bash
707
- python3 hooks/install_quick_action.py
761
+ vocalize integrate claude
708
762
  ```
709
763
 
710
- This copies the bundles from `hooks/quick_actions/` into
711
- `~/Library/Services/` with this machine's absolute `vocalize`, `claude`,
712
- and helper paths baked in, then refreshes the Services registry. Run it
713
- from a normal terminal — its PATH is what gets captured. To trigger the
714
- actions from the keyboard, assign shortcuts under System Settings →
715
- Keyboard → Keyboard Shortcuts → Services (Stop Vocalize is worth a
764
+ This installs the `/speak` skill plus the four Quick Action bundles into
765
+ `~/Library/Services/`, with this machine's `vocalize`, `claude`, and
766
+ helper paths baked in (the stable symlinks, not their resolved targets —
767
+ survives a `brew upgrade claude-code`), then refreshes the Services
768
+ registry. Run it from a normal terminal — its PATH is what gets captured.
769
+ To trigger the actions from the keyboard, assign shortcuts under System
770
+ Settings → Keyboard → Keyboard Shortcuts → Services (Stop Vocalize is worth a
716
771
  shortcut of its own so you can silence a read from anywhere).
717
772
 
718
773
  Some Electron apps (Claude Code desktop among them) don't expose the
@@ -924,9 +979,10 @@ model.
924
979
  there. Use `--no-play` and open the saved file with whatever's on hand.
925
980
  - **The disk cache under `~/.cache/vocalize` grows unbounded.** It's
926
981
  content-addressed (keyed by a hash of text, voice, model, format, and
927
- speed),
928
- so it's always safe to delete some or all of it — nothing will break,
929
- you'll just re-pay for a re-synthesized clip.
982
+ speed), so it's always safe to delete — except `bin/`, which holds
983
+ Vocalize Recorder and is tied to your microphone grant; deleting that
984
+ forces a rebuild and a re-grant. Delete anything else and nothing will
985
+ break, you'll just re-pay for a re-synthesized clip.
930
986
  - **`--api-key` on the command line is visible to other local processes**
931
987
  (anything that can run `ps`). Prefer `vocalize auth login`, the
932
988
  `ELEVENLABS_API_KEY` environment variable, or a `.env` file instead.
@@ -477,13 +477,24 @@ more accurate) or `--model base.en` (~141 MB, fastest, least accurate).
477
477
  ### The hotkey
478
478
 
479
479
  ```bash
480
- python3 hooks/install_quick_action.py
480
+ vocalize app install
481
+ ```
482
+
483
+ installs and starts **Vocalize.app**, a menu-bar app that owns the
484
+ dictation hotkey (and speak/stop) system-wide, no Services menu or
485
+ per-app shortcut assignment needed — see [docs/app.md](docs/app.md) for
486
+ install, uninstall, status and restart. `vocalize dictate` is the same
487
+ command from a terminal, if you'd rather trigger it that way.
488
+
489
+ Prefer a Quick Action instead? That still works:
490
+
491
+ ```bash
492
+ vocalize integrate claude
481
493
  ```
482
494
 
483
495
  then assign a shortcut under **System Settings › Keyboard › Keyboard
484
496
  Shortcuts › Services › Text › "Dictate with Vocalize"** — ⌃⌥⌘D is free by
485
- default and a sensible pick. `vocalize dictate` is the same command from a
486
- terminal, if you'd rather trigger it that way.
497
+ default and a sensible pick.
487
498
 
488
499
  ### How a dictation works
489
500
 
@@ -553,7 +564,7 @@ cues = "sounds" # "sounds" | "words" | "both" — speak "Start."/"Stopped
553
564
  | `cleanup` | `off`, `local` (0.13), `claude-cli`, `anthropic`; an old `true`/`false` reads as `claude-cli`/`off` | `off` |
554
565
  | `verbatim` | `true` / `false` — keep every word even when cleanup is on | `false` |
555
566
  | `beam_size` | integer, 1–8 | `5` |
556
- | `paste` | reserved — not implemented in 0.10.0 | `false` |
567
+ | `paste` | `true` / `false` — paste into the app you dictated in, after copying (see docs/dictation.md § Auto-paste) | `false` |
557
568
  | `max_seconds` | integer, 1–600 | `120` |
558
569
  | `sounds` | `true` / `false` | `true` |
559
570
  | `cues` | `sounds`, `words`, `both` | `sounds` |
@@ -573,6 +584,37 @@ set it:
573
584
  input_device = "MacBook Pro Microphone"
574
585
  ```
575
586
 
587
+ ### Configuration: the `[app]` table
588
+
589
+ The chords the menu-bar app owns, system-wide. A change needs
590
+ `vocalize app restart` before the app picks it up.
591
+
592
+ ```toml
593
+ [app]
594
+ dictate = "ctrl+alt+cmd+d" # start/stop dictation
595
+ dictate_mode = "toggle" # toggle | hold
596
+ speak = "ctrl+alt+cmd+s" # speak the selection
597
+ stop = "ctrl+alt+cmd+x" # stop whatever is playing
598
+ ```
599
+
600
+ | Key | Allowed values | Default |
601
+ |---|---|---|
602
+ | `dictate` | a chord, or `""` to disable | `ctrl+alt+cmd+d` |
603
+ | `dictate_mode` | `toggle`, `hold` | `toggle` |
604
+ | `speak` | a chord, or `""` to disable | `ctrl+alt+cmd+s` |
605
+ | `stop` | a chord, or `""` to disable | `ctrl+alt+cmd+x` |
606
+
607
+ **Chord grammar:** tokens joined by `+`. Modifiers are `ctrl`, `alt`,
608
+ `cmd`, `shift` (long spellings `control`, `option`, `command` also work);
609
+ the key is one of `a`-`z`, `0`-`9`, `f1`-`f12`. A chord must include
610
+ `ctrl` or `cmd` — macOS refuses to register the rest. The three chords
611
+ must differ from each other, and `""` disables one.
612
+
613
+ A bad chord is a `ConfigError` naming the key, the value and the legal
614
+ keys — and, like any bad config value, it stops the command you ran, so
615
+ fix it before anything else works. `vocalize settings` prints the four
616
+ `app.*` values in their canonical spelling.
617
+
576
618
  ### `vocalize status`
577
619
 
578
620
  ```bash
@@ -595,6 +637,17 @@ get four permanent red rows for a feature nobody asked for):
595
637
  providers and dictation both — is `ok`, 1 otherwise, so it composes with
596
638
  `&&` in a script.
597
639
 
640
+ ### `vocalize doctor`
641
+
642
+ Everything `status` checks plus the machine itself: the CLI's own path and
643
+ start-up time, `uv`, `swiftc`, `claude`, the console script's interpreter,
644
+ Hammerspoon, the app bundle and the notes folder. Every row regardless of the
645
+ chain, exit 1 when any row fails. `--json` for scripts.
646
+
647
+ ```bash
648
+ vocalize doctor
649
+ ```
650
+
598
651
  ### Continuing an interrupted read
599
652
 
600
653
  Starting a dictation stops any read in progress, but doesn't throw the rest
@@ -604,6 +657,7 @@ where it stopped, and once your transcript has landed, vocalize asks:
604
657
  up after 15 seconds (counted as no). From a terminal, the same thing is:
605
658
 
606
659
  ```bash
660
+ vocalize pause # pause live playback and save your place
607
661
  vocalize resume # continue where the last read left off
608
662
  vocalize resume --forget # discard it instead
609
663
  ```
@@ -668,15 +722,16 @@ Four Services let you use vocalize from any app without a terminal:
668
722
  Install all four:
669
723
 
670
724
  ```bash
671
- python3 hooks/install_quick_action.py
725
+ vocalize integrate claude
672
726
  ```
673
727
 
674
- This copies the bundles from `hooks/quick_actions/` into
675
- `~/Library/Services/` with this machine's absolute `vocalize`, `claude`,
676
- and helper paths baked in, then refreshes the Services registry. Run it
677
- from a normal terminal — its PATH is what gets captured. To trigger the
678
- actions from the keyboard, assign shortcuts under System Settings →
679
- Keyboard → Keyboard Shortcuts → Services (Stop Vocalize is worth a
728
+ This installs the `/speak` skill plus the four Quick Action bundles into
729
+ `~/Library/Services/`, with this machine's `vocalize`, `claude`, and
730
+ helper paths baked in (the stable symlinks, not their resolved targets —
731
+ survives a `brew upgrade claude-code`), then refreshes the Services
732
+ registry. Run it from a normal terminal — its PATH is what gets captured.
733
+ To trigger the actions from the keyboard, assign shortcuts under System
734
+ Settings → Keyboard → Keyboard Shortcuts → Services (Stop Vocalize is worth a
680
735
  shortcut of its own so you can silence a read from anywhere).
681
736
 
682
737
  Some Electron apps (Claude Code desktop among them) don't expose the
@@ -888,9 +943,10 @@ model.
888
943
  there. Use `--no-play` and open the saved file with whatever's on hand.
889
944
  - **The disk cache under `~/.cache/vocalize` grows unbounded.** It's
890
945
  content-addressed (keyed by a hash of text, voice, model, format, and
891
- speed),
892
- so it's always safe to delete some or all of it — nothing will break,
893
- you'll just re-pay for a re-synthesized clip.
946
+ speed), so it's always safe to delete — except `bin/`, which holds
947
+ Vocalize Recorder and is tied to your microphone grant; deleting that
948
+ forces a rebuild and a re-grant. Delete anything else and nothing will
949
+ break, you'll just re-pay for a re-synthesized clip.
894
950
  - **`--api-key` on the command line is visible to other local processes**
895
951
  (anything that can run `ps`). Prefer `vocalize auth login`, the
896
952
  `ELEVENLABS_API_KEY` environment variable, or a `.env` file instead.
@@ -0,0 +1,205 @@
1
+ # The menu-bar app
2
+
3
+ `Vocalize.app` is a small Swift menu-bar app that owns the dictation and
4
+ speak-selection hotkeys system-wide. It replaces both the Hammerspoon
5
+ config and the Quick Action as the way to trigger vocalize from the
6
+ keyboard — it runs all the time, as a LaunchAgent, so a hotkey works from
7
+ any app without a terminal or a Service menu.
8
+
9
+ It does one job: listen for its three chords, then run the matching
10
+ `vocalize` command (`dictate`, `clip`, `stop`) as a subprocess. It never
11
+ touches your microphone or clipboard itself and never reads the audio or
12
+ the transcript — `vocalize` does that work, same as if you'd typed the
13
+ command.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ vocalize app install
19
+ ```
20
+
21
+ This:
22
+
23
+ 1. Checks that the `vocalize` you're running right now is one the app can
24
+ find. The app only looks in `~/.local/bin`, `/opt/homebrew/bin` and
25
+ `/usr/local/bin`; if yours isn't one of those, install prints a
26
+ `defaults write` line (see [Pointing the app at a different
27
+ vocalize](#pointing-the-app-at-a-different-vocalize) below) and stops —
28
+ pass `--yes` to print the same line and continue anyway.
29
+ 2. Warns if Hammerspoon is running, or if the "Dictate with Vocalize"
30
+ Quick Action has a keyboard shortcut on one of the app's chords —
31
+ either can steal it (see
32
+ [Conflicts](#conflicts-hammerspoon-and-the-old-quick-action)).
33
+ 3. Builds `Vocalize.app` under `~/Library/Application Support/vocalize`
34
+ if it isn't already current — see [The re-grant
35
+ rule](#the-re-grant-rule) for what happens when a build replaces one
36
+ that was already there.
37
+ 4. Writes the LaunchAgent plist at
38
+ `~/Library/LaunchAgents/cards.arda.vocalize.app.plist` and loads it,
39
+ so the app starts now and again at every login.
40
+
41
+ It asks to confirm first, listing exactly what it's about to write.
42
+ `--yes` skips that prompt.
43
+
44
+ ## Status
45
+
46
+ ```bash
47
+ vocalize app status
48
+ vocalize app status --json
49
+ ```
50
+
51
+ Reports what's built, whether the LaunchAgent is loaded, and what the
52
+ app itself last wrote about its hotkeys and Accessibility grant:
53
+
54
+ | Row | Reports |
55
+ |---|---|
56
+ | `bundle` | `current`, `stale`, or `not built` — read-only, this never triggers a build |
57
+ | `agent` | `loaded`, `not running`, or `unknown` — from `launchctl`; a result it can't parse reads as `unknown`, never `not running` |
58
+ | `accessibility` | `granted`, `not granted`, or `unknown` |
59
+ | `hotkeys` | `ok`, `failed:<name>`, or `unknown` — which chord, if any, failed to register |
60
+ | `hotkey_backend` | `carbon` or `monitor` — which macOS API is handling the keys |
61
+ | `vocalize` | the path the app resolved, or `none` |
62
+
63
+ The last four come from the app's own status file and can be stale
64
+ between runs — the app only rewrites it when something changes.
65
+
66
+ `--json` prints the same six keys as one object. Exit code is 0 when
67
+ `bundle` is `current` and `agent` is `loaded`, 1 otherwise, so it
68
+ composes with `&&` in a script.
69
+
70
+ ## Chords
71
+
72
+ Three chords, set under `[app]` in `~/.config/vocalize/config.toml`:
73
+
74
+ | Key | What it does | Default |
75
+ |---|---|---|
76
+ | `dictate` | start/stop dictation | `ctrl+alt+cmd+d` |
77
+ | `dictate_mode` | `toggle` or `hold` | `toggle` |
78
+ | `speak` | speak the selection | `ctrl+alt+cmd+s` |
79
+ | `stop` | stop whatever is playing | `ctrl+alt+cmd+x` |
80
+
81
+ ```toml
82
+ [app]
83
+ dictate = "ctrl+alt+cmd+d"
84
+ dictate_mode = "toggle"
85
+ speak = "ctrl+alt+cmd+s"
86
+ stop = "ctrl+alt+cmd+x"
87
+ ```
88
+
89
+ Tokens join with `+`. Modifiers are `ctrl`, `alt`, `cmd`, `shift` (or the
90
+ long `control`, `option`, `command`); the key is one of `a`-`z`, `0`-`9`,
91
+ `f1`-`f12`. Every chord needs `ctrl` or `cmd`, because macOS refuses to
92
+ register the rest. The three must differ from each other; `""` disables
93
+ one.
94
+
95
+ A chord the parser rejects is a config error, and config is read by
96
+ almost every command — so a typo here stops `vocalize speak` and
97
+ `vocalize status` too, with a message naming the key and the legal keys.
98
+ `vocalize doctor` still runs, and reports it as a failed `config file`
99
+ row. The running app keeps the chords it already registered until the
100
+ file is valid again and you run `vocalize app restart`.
101
+
102
+ ## Restart
103
+
104
+ ```bash
105
+ vocalize app restart
106
+ ```
107
+
108
+ Stops and starts the running app without touching the LaunchAgent or
109
+ rebuilding anything — the equivalent of quitting it from its own menu
110
+ and having it come back. Use this after a config change (a new chord
111
+ under `[app]`) instead of a full `install`.
112
+
113
+ ## Uninstall
114
+
115
+ ```bash
116
+ vocalize app uninstall
117
+ ```
118
+
119
+ Unloads the app and removes what `install` put on this machine: the
120
+ bundle, the LaunchAgent plist, the app's status and log files, and the
121
+ `VocalizeBinary` override if one was set. It lists exactly what it's
122
+ about to remove and asks first; `--yes` skips the prompt.
123
+
124
+ The Accessibility entry itself stays in System Settings — macOS keeps a
125
+ record of every app that ever asked, uninstalled or not — remove it
126
+ there if you want it gone too. Dictation and the recorder's own
127
+ microphone grant are a separate install (`vocalize local install --stt`)
128
+ and are untouched by this command.
129
+
130
+ ## The re-grant rule
131
+
132
+ The app's Accessibility permission is tied to its ad-hoc code signature,
133
+ the same way the recorder's microphone permission is (see
134
+ [docs/dictation.md](dictation.md#rebuilds-and-re-granting-the-microphone)).
135
+ `install` only rebuilds the bundle — and only resets the grant — when one
136
+ of these is true:
137
+
138
+ - the Swift source changed since the bundle was last built, or
139
+ - the bundle or its stamp file is missing (deleted by hand, or by
140
+ `app uninstall`, or by clearing `~/Library/Application Support`
141
+ yourself).
142
+
143
+ Either one produces a new signature, so `install` runs
144
+ `tccutil reset Accessibility cards.arda.vocalize.app` before loading it
145
+ and prints:
146
+
147
+ ```
148
+ Vocalize.app was rebuilt — its Accessibility grant was reset; re-grant it in
149
+ System Settings › Privacy & Security › Accessibility the next time it asks
150
+ ```
151
+
152
+ A plain `install` on an already-current bundle never touches `tccutil` —
153
+ most upgrades don't touch the Swift source, so most re-installs cost you
154
+ nothing.
155
+
156
+ ## Pointing the app at a different vocalize
157
+
158
+ The app doesn't search your `PATH` — it only checks three fixed
159
+ locations. If `install` reports your `vocalize` isn't one of them, run
160
+ the line it prints:
161
+
162
+ ```bash
163
+ defaults write cards.arda.vocalize.app VocalizeBinary /path/to/vocalize
164
+ ```
165
+
166
+ Uninstalling removes this override automatically.
167
+
168
+ ## The Setup tab
169
+
170
+ `vocalize portal` has a **Setup** tab, after Local. It walks the nine
171
+ setup steps — toolchain, building both bundles, the login item, chords,
172
+ the model download, the microphone, `/speak` and Quick Actions,
173
+ Accessibility, and a self-test — as a checklist built from the readiness
174
+ rows the sidebar shows (step 1 points at `vocalize doctor`), so it's always
175
+ current with what's actually on the machine. Its "Install the app" button drives this same `app install`
176
+ sequence (build, plist, bootout, bootstrap, and the Accessibility reset on
177
+ a rebuild) through the portal instead of the CLI; the GUI-only steps still
178
+ print from `vocalize integrate claude`, not the page.
179
+
180
+ ## Quit stays quit
181
+
182
+ The app's own menu has a Quit item, and quitting it stays quit — it does
183
+ not relaunch itself the next time it crashes on purpose or the way a
184
+ typical LaunchAgent does on every exit. It comes back at your next login,
185
+ or the next time you run `vocalize app restart` or `install`.
186
+
187
+ ## Conflicts: Hammerspoon and the old Quick Action
188
+
189
+ Two older ways of binding the same keys can steal a chord out from under
190
+ the app, so its hotkey silently does nothing:
191
+
192
+ - **Hammerspoon**, if it's still running and still has the old
193
+ `hs.hotkey.bind` lines for dictate/speak. `install` checks for a
194
+ running Hammerspoon and, if found, tells you to remove the
195
+ `ctrl-alt-cmd-S` and `ctrl-alt-cmd-X` bind lines from
196
+ `~/.hammerspoon/init.lua` and reload Hammerspoon.
197
+ - **A Services keyboard shortcut** on the "Dictate with Vocalize" or
198
+ "Stop Vocalize" Quick Action. `vocalize integrate claude` installs both
199
+ on purpose, and keeping them is fine — they stay reachable from the
200
+ Services menu. What conflicts is *assigning* ctrl-alt-cmd-D or
201
+ ctrl-alt-cmd-X to one in System Settings › Keyboard › Keyboard
202
+ Shortcuts › Services: the assignment wins and the app's hotkey never
203
+ fires. `install` says so when the Quick Action is present.
204
+
205
+ Neither check blocks the install; they're warnings, not a hard stop.