claude-code-tools 1.19.1__tar.gz → 1.19.3__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 (137) hide show
  1. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/PKG-INFO +2 -1
  2. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/__init__.py +1 -1
  3. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/app.py +9 -1
  4. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/engine_parakeet.py +24 -0
  5. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/hotkey.py +120 -0
  6. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/pyproject.toml +6 -2
  7. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/.gitignore +0 -0
  8. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/LICENSE +0 -0
  9. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/README.md +0 -0
  10. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/action_rpc.py +0 -0
  11. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/__init__.py +0 -0
  12. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/backends.py +0 -0
  13. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/cli.py +0 -0
  14. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/config.py +0 -0
  15. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/convert.py +0 -0
  16. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/discord_bot.py +0 -0
  17. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/locking.py +0 -0
  18. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/paths.py +0 -0
  19. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/registry.py +0 -0
  20. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/session.py +0 -0
  21. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/store.py +0 -0
  22. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/tmux.py +0 -0
  23. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/agent_tunnel/trust.py +0 -0
  24. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/aichat.py +0 -0
  25. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/claude_continue.py +0 -0
  26. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_continue.py +0 -0
  27. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server.py +0 -0
  28. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_cli.py +0 -0
  29. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_fingerprint.py +0 -0
  30. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_generation.py +0 -0
  31. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_legacy.py +0 -0
  32. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_models.py +0 -0
  33. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_process.py +0 -0
  34. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_reservation.py +0 -0
  35. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_retry.py +0 -0
  36. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_reuse.py +0 -0
  37. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_state.py +0 -0
  38. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_supervisor.py +0 -0
  39. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/codex_server_worker.py +0 -0
  40. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/config.py +0 -0
  41. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/csv2gsheet.py +0 -0
  42. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/delete_session.py +0 -0
  43. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/dotenv_vault.py +0 -0
  44. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/env_safe.py +0 -0
  45. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/export_all.py +0 -0
  46. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/export_claude_session.py +0 -0
  47. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/export_codex_session.py +0 -0
  48. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/export_session.py +0 -0
  49. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/find_claude_session.py +0 -0
  50. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/find_codex_session.py +0 -0
  51. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/find_original_session.py +0 -0
  52. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/find_session.py +0 -0
  53. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/find_trimmed_sessions.py +0 -0
  54. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/fix_session.py +0 -0
  55. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/gdoc2docx.py +0 -0
  56. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/gdoc2md.py +0 -0
  57. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/gsheet2csv.py +0 -0
  58. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/md2gdoc.py +0 -0
  59. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/msg/__init__.py +0 -0
  60. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/msg/cli.py +0 -0
  61. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/msg/hooks.py +0 -0
  62. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/msg/models.py +0 -0
  63. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/msg/prompt_detect.py +0 -0
  64. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/msg/store.py +0 -0
  65. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/msg/watcher.py +0 -0
  66. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/node_menu_ui.py +0 -0
  67. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/port_claude_noise.py +0 -0
  68. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/port_claude_to_codex.py +0 -0
  69. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/port_codex_flatten.py +0 -0
  70. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/port_codex_to_claude.py +0 -0
  71. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/port_service.py +0 -0
  72. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/resolve_session.py +0 -0
  73. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/resolve_session_names.py +0 -0
  74. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/resolve_session_render.py +0 -0
  75. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/search_index.py +0 -0
  76. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/session_lineage.py +0 -0
  77. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/session_menu.py +0 -0
  78. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/session_menu_cli.py +0 -0
  79. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/session_utils.py +0 -0
  80. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/smart_trim.py +0 -0
  81. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/smart_trim_core.py +0 -0
  82. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/tmux_cli_controller.py +0 -0
  83. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/tmux_execution_helpers.py +0 -0
  84. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/tmux_remote_controller.py +0 -0
  85. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/trim_in_place.py +0 -0
  86. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/trim_session.py +0 -0
  87. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/trim_session_claude.py +0 -0
  88. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/trim_session_codex.py +0 -0
  89. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/__init__.py +0 -0
  90. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/cli.py +0 -0
  91. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/config.py +0 -0
  92. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/engine_parakeet_mlx.py +0 -0
  93. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/engines.py +0 -0
  94. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/inject.py +0 -0
  95. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/logic.py +0 -0
  96. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/voice_type/overlay.py +0 -0
  97. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli.py +0 -0
  98. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli_contract.py +0 -0
  99. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli_formatting.py +0 -0
  100. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli_identity_policy.py +0 -0
  101. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli_manifest.py +0 -0
  102. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli_projection.py +0 -0
  103. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli_rendering.py +0 -0
  104. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli_snapshots.py +0 -0
  105. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_cli_store_backends.py +0 -0
  106. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_processes.py +0 -0
  107. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_runs.py +0 -0
  108. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_store_io.py +0 -0
  109. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/claude_code_tools/workflow_validation.py +0 -0
  110. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/agent-tunnel-spec.md +0 -0
  111. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/ai-agent-writing-skills.md +0 -0
  112. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/cc-codex-instructions.md +0 -0
  113. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/claude-code-chutes.md +0 -0
  114. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/claude-code-tmux-tutorials.md +0 -0
  115. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/dot-zshrc.md +0 -0
  116. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/ensemble-ideas.md +0 -0
  117. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/example-with-images.md +0 -0
  118. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/find-claude-session.md +0 -0
  119. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/intercom-spec.md +0 -0
  120. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/linked-in-20260102.md +0 -0
  121. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/lmsh.md +0 -0
  122. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/local-llm-setup.md +0 -0
  123. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/msg-phase-a-plan.md +0 -0
  124. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/msg-plan-v2.md +0 -0
  125. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/paper-writing-style-transfer-tools.md +0 -0
  126. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/reddit-aichat-resume-v2.md +0 -0
  127. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/reddit-aichat-resume.md +0 -0
  128. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/reddit-aichat.md +0 -0
  129. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/reddit-post.md +0 -0
  130. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/rollover-details.md +0 -0
  131. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/tmux-cli-instructions.md +0 -0
  132. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/docs/vault-documentation.md +0 -0
  133. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/hatch_build.py +0 -0
  134. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/node_ui/action_config.js +0 -0
  135. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/node_ui/menu.js +0 -0
  136. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/node_ui/package-lock.json +0 -0
  137. {claude_code_tools-1.19.1 → claude_code_tools-1.19.3}/node_ui/package.json +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: claude-code-tools
3
- Version: 1.19.1
3
+ Version: 1.19.3
4
4
  Summary: Collection of tools for working with Claude Code
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -26,6 +26,7 @@ Provides-Extra: voice
26
26
  Requires-Dist: moonshine-voice>=0.0.69; extra == 'voice'
27
27
  Requires-Dist: pynput>=1.7.7; extra == 'voice'
28
28
  Provides-Extra: voice-mlx
29
+ Requires-Dist: numba>=0.61; extra == 'voice-mlx'
29
30
  Requires-Dist: numpy>=1.26.0; extra == 'voice-mlx'
30
31
  Requires-Dist: parakeet-mlx>=0.5.0; extra == 'voice-mlx'
31
32
  Requires-Dist: pynput>=1.7.7; extra == 'voice-mlx'
@@ -1,3 +1,3 @@
1
1
  """Claude Code Tools - Collection of utilities for Claude Code."""
2
2
 
3
- __version__ = "1.19.1"
3
+ __version__ = "1.19.3"
@@ -571,12 +571,20 @@ class VoiceTypeApp:
571
571
  def _start_hotkey_listener(self): # noqa: ANN202
572
572
  """Start the global hotkey listener; invalid chords degrade.
573
573
 
574
+ Missing macOS permissions are reported LOUDLY first: a launch
575
+ context without Input Monitoring gets a dead event tap with no
576
+ error from the OS, which previously looked exactly like "the
577
+ hotkey just doesn't work".
578
+
574
579
  Each configured chord is validated independently, so one
575
580
  malformed OPTIONAL binding (paste/cancel) only disables itself
576
581
  — a typo in cancel_hotkey must never take the toggle hotkey
577
582
  (and with it all of toggle mode) down.
578
583
  """
579
- from .hotkey import parse_hotkey, start_hotkeys
584
+ from .hotkey import check_permissions, parse_hotkey, start_hotkeys
585
+
586
+ for warning in check_permissions():
587
+ self._status(f"WARNING: {warning}")
580
588
 
581
589
  candidates: list[tuple[str, tuple]] = [
582
590
  ("toggle", (self.cfg.hotkey, self.toggle))
@@ -525,6 +525,25 @@ class ParakeetEngine:
525
525
  except Exception:
526
526
  pass
527
527
 
528
+ def _report_decode_time(self, audio_secs: float, elapsed: float) -> None:
529
+ """Log how long a decode took, and its speed vs. real time.
530
+
531
+ Makes latency visible per utterance: a healthy MLX/CPU decode
532
+ runs at many times real time, so a drop toward 1x (or below) is
533
+ an at-a-glance signal that something — memory pressure, thermal
534
+ throttling — is slowing transcription down.
535
+ """
536
+ if elapsed > 0:
537
+ self._report(
538
+ f"transcribed {audio_secs:.1f}s of audio in "
539
+ f"{elapsed:.2f}s ({audio_secs / elapsed:.0f}x realtime)"
540
+ )
541
+ else:
542
+ self._report(
543
+ f"transcribed {audio_secs:.1f}s of audio in "
544
+ f"{elapsed:.2f}s"
545
+ )
546
+
528
547
  def _safe_call(self, fn: Callable[[], None], name: str) -> None:
529
548
  """Invoke a client callback; never let its exception escape.
530
549
 
@@ -843,11 +862,13 @@ class ParakeetEngine:
843
862
  """
844
863
  secs = len(take) / SAMPLE_RATE
845
864
  self._report(f"transcribing {secs:.1f}s take...")
865
+ t0 = time.monotonic()
846
866
  try:
847
867
  text = self.transcribe(take, SAMPLE_RATE)
848
868
  except Exception as e:
849
869
  self._report(f"parakeet decode error: {e}")
850
870
  return
871
+ self._report_decode_time(secs, time.monotonic() - t0)
851
872
  if text:
852
873
  if self._closed.is_set():
853
874
  return
@@ -949,11 +970,14 @@ class ParakeetEngine:
949
970
  self._vad.pop()
950
971
  if segment is None or segment.ndim != 1 or segment.size == 0:
951
972
  continue
973
+ seg_secs = segment.size / SAMPLE_RATE
974
+ t0 = time.monotonic()
952
975
  try:
953
976
  text = self.transcribe(segment, SAMPLE_RATE)
954
977
  except Exception as e:
955
978
  self._report(f"parakeet decode error: {e}")
956
979
  continue
980
+ self._report_decode_time(seg_secs, time.monotonic() - t0)
957
981
  if text:
958
982
  self._safe_call(
959
983
  lambda t=text: on_utterance(t), "on_utterance"
@@ -303,6 +303,126 @@ def record_hotkey(timeout: float = 15.0) -> str | None:
303
303
  return result.get("chord")
304
304
 
305
305
 
306
+ def check_permissions() -> list[str]:
307
+ """Report missing macOS permissions that silently kill hotkeys.
308
+
309
+ macOS attributes keystroke access to the LAUNCH CONTEXT (the
310
+ terminal app — or tmux, when launched inside tmux), and a context
311
+ without Input Monitoring gets a dead event tap with no error: the
312
+ app looks healthy but hotkeys simply never fire. Observed in the
313
+ field when a rebuilt venv/new launch context lost the prior grant.
314
+
315
+ Returns human-readable warnings (empty ONLY when every check ran
316
+ and every permission is granted — an unavailable module/symbol or
317
+ a probe that throws produces a "could not verify" warning instead
318
+ of silently passing) and asks macOS to register/prompt for the
319
+ missing access so the user can grant it in System Settings.
320
+ """
321
+ if sys.platform != "darwin":
322
+ return []
323
+ warnings: list[str] = []
324
+ warnings.extend(_check_input_monitoring())
325
+ warnings.extend(_check_accessibility())
326
+ return warnings
327
+
328
+
329
+ def _check_input_monitoring() -> list[str]:
330
+ """Probe the Input Monitoring grant; never silently swallow failure."""
331
+ try:
332
+ import Quartz
333
+ except Exception as e: # pragma: no cover - environment-specific
334
+ return [
335
+ "could not verify Input Monitoring permission (Quartz "
336
+ f"bridge unavailable: {e!r}) — hotkeys may silently not "
337
+ "work. Check that pyobjc is installed correctly."
338
+ ]
339
+ preflight = getattr(Quartz, "CGPreflightListenEventAccess", None)
340
+ if preflight is None:
341
+ return [
342
+ "could not verify Input Monitoring permission (this "
343
+ "macOS/pyobjc lacks CGPreflightListenEventAccess) — "
344
+ "hotkeys may silently not work."
345
+ ]
346
+ try:
347
+ granted = bool(preflight())
348
+ except Exception as e:
349
+ return [
350
+ "could not verify Input Monitoring permission (the "
351
+ f"CGPreflightListenEventAccess probe failed: {e!r}) — "
352
+ "hotkeys may silently not work."
353
+ ]
354
+ if granted:
355
+ return []
356
+ warnings = [
357
+ "Input Monitoring permission MISSING for this launch "
358
+ "context — hotkeys will NOT work. Grant it in System "
359
+ "Settings > Privacy & Security > Input Monitoring "
360
+ "(to your terminal app, and to tmux if you launched "
361
+ "inside tmux), then restart voice-type."
362
+ ]
363
+ # Nonfatal: registration just makes the app appear in System
364
+ # Settings so the user can flip the toggle — but a failure is
365
+ # still reported, never silently suppressed.
366
+ try:
367
+ Quartz.CGRequestListenEventAccess()
368
+ except Exception as e:
369
+ warnings.append(
370
+ "requesting Input Monitoring registration failed "
371
+ f"({e!r}); add your terminal app manually in System "
372
+ "Settings > Privacy & Security > Input Monitoring."
373
+ )
374
+ return warnings
375
+
376
+
377
+ def _check_accessibility() -> list[str]:
378
+ """Probe the Accessibility grant; never silently swallow failure."""
379
+ try:
380
+ from ApplicationServices import AXIsProcessTrusted
381
+ except Exception as e: # pragma: no cover - environment-specific
382
+ return [
383
+ "could not verify Accessibility permission "
384
+ f"(ApplicationServices bridge unavailable: {e!r}) — "
385
+ "typing into other apps may silently not work. Check "
386
+ "that pyobjc is installed correctly."
387
+ ]
388
+ try:
389
+ trusted = bool(AXIsProcessTrusted())
390
+ except Exception as e:
391
+ return [
392
+ "could not verify Accessibility permission (the "
393
+ f"AXIsProcessTrusted probe failed: {e!r}) — typing into "
394
+ "other apps may silently not work."
395
+ ]
396
+ if trusted:
397
+ return []
398
+ warnings = [
399
+ "Accessibility permission MISSING for this launch "
400
+ "context — typing into other apps will NOT work. "
401
+ "Grant it in System Settings > Privacy & Security > "
402
+ "Accessibility, then restart voice-type."
403
+ ]
404
+ # Nonfatal: the prompt request registers the app in System
405
+ # Settings (and may show the system grant dialog) so the user can
406
+ # flip the toggle — a failure is still reported, never silently
407
+ # suppressed.
408
+ try:
409
+ from ApplicationServices import (
410
+ AXIsProcessTrustedWithOptions,
411
+ kAXTrustedCheckOptionPrompt,
412
+ )
413
+
414
+ AXIsProcessTrustedWithOptions(
415
+ {kAXTrustedCheckOptionPrompt: True}
416
+ )
417
+ except Exception as e:
418
+ warnings.append(
419
+ "requesting Accessibility registration failed "
420
+ f"({e!r}); add your terminal app manually in System "
421
+ "Settings > Privacy & Security > Accessibility."
422
+ )
423
+ return warnings
424
+
425
+
306
426
  def start_hotkeys(bindings: list[tuple]): # noqa: ANN201
307
427
  """Start one global listener for several chords; returns ``stop()``-able.
308
428
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "claude-code-tools"
3
- version = "1.19.1"
3
+ version = "1.19.3"
4
4
  description = "Collection of tools for working with Claude Code"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -44,6 +44,10 @@ voice-parakeet = [
44
44
  voice-mlx = [
45
45
  "pynput>=1.7.7",
46
46
  "parakeet-mlx>=0.5.0",
47
+ # Resolver guard: without it uv can backtrack through parakeet-mlx
48
+ # -> librosa -> numba to a 2021 llvmlite that cannot build on
49
+ # modern Python ("Cannot install on Python version 3.13...").
50
+ "numba>=0.61",
47
51
  # sherpa-onnx supplies the Silero VAD used for segmentation.
48
52
  "sherpa-onnx>=1.12.0,<1.13",
49
53
  "sherpa-onnx-core>=1.12.0,<1.13",
@@ -127,7 +131,7 @@ markers = [
127
131
 
128
132
  [tool.commitizen]
129
133
  name = "cz_conventional_commits"
130
- version = "1.19.1"
134
+ version = "1.19.3"
131
135
  tag_format = "v$version"
132
136
  version_files = [
133
137
  "pyproject.toml:version",