meshcode 2.11.9__tar.gz → 2.11.11__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 (222) hide show
  1. {meshcode-2.11.9 → meshcode-2.11.11}/PKG-INFO +1 -1
  2. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/__init__.py +1 -1
  3. meshcode-2.11.11/meshcode/__main__.py +42 -0
  4. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/comms_v4.py +115 -3
  5. meshcode-2.11.11/meshcode/doctor.py +277 -0
  6. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/server.py +25 -2
  7. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/run_agent.py +72 -8
  8. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode.egg-info/PKG-INFO +1 -1
  9. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode.egg-info/SOURCES.txt +5 -0
  10. {meshcode-2.11.9 → meshcode-2.11.11}/pyproject.toml +1 -1
  11. meshcode-2.11.11/tests/test_color_truecolor.py +83 -0
  12. meshcode-2.11.11/tests/test_doctor.py +123 -0
  13. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_install_guard.py +68 -10
  14. meshcode-2.11.11/tests/test_setup_path.py +66 -0
  15. {meshcode-2.11.9 → meshcode-2.11.11}/README.md +0 -0
  16. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/_stop_hook_template.py +0 -0
  17. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/ascii_art.py +0 -0
  18. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/cli.py +0 -0
  19. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/compat.py +0 -0
  20. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/daemon.py +0 -0
  21. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/error_hints.py +0 -0
  22. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/exceptions.py +0 -0
  23. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/invites.py +0 -0
  24. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/launcher.py +0 -0
  25. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/launcher_install.py +0 -0
  26. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/__init__.py +0 -0
  27. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/__main__.py +0 -0
  28. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/backend.py +0 -0
  29. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/realtime.py +0 -0
  30. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  31. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/test_backend.py +0 -0
  32. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  33. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  34. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  35. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  36. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  37. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/preferences.py +0 -0
  38. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/protocol_handler.py +0 -0
  39. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/protocol_v2.py +0 -0
  40. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/quickstart.py +0 -0
  41. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/scripts/check_secrets.py +0 -0
  42. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/scripts/race_rate_harness.py +0 -0
  43. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/secrets.py +0 -0
  44. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/self_update.py +0 -0
  45. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/setup_clients.py +0 -0
  46. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/supervisor.py +0 -0
  47. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode/upload.py +0 -0
  48. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/comms_v4.py +0 -0
  49. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/__init__.py +0 -0
  50. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
  51. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/cli.py +0 -0
  52. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
  53. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/compat.py +0 -0
  54. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
  55. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
  56. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/invites.py +0 -0
  57. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/launcher.py +0 -0
  58. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
  59. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  60. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  61. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
  62. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  63. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
  64. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  65. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  66. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  67. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/preferences.py +0 -0
  68. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
  69. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
  70. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
  71. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/secrets.py +0 -0
  72. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/self_update.py +0 -0
  73. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
  74. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
  75. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/meshcode/upload.py +0 -0
  76. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/scripts/sentinel.py +0 -0
  77. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_core.py +0 -0
  78. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
  79. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
  80. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
  81. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
  82. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
  83. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
  84. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
  85. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
  86. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
  87. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
  88. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
  89. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
  90. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
  91. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
  92. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
  93. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
  94. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
  95. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
  96. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
  97. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
  98. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
  99. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
  100. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
  101. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
  102. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
  103. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
  104. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
  105. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
  106. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  107. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
  108. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
  109. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
  110. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
  111. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
  112. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
  113. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
  114. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
  115. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
  116. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/comms_v4.py +0 -0
  117. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/__init__.py +0 -0
  118. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
  119. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/cli.py +0 -0
  120. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
  121. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/compat.py +0 -0
  122. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
  123. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
  124. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/invites.py +0 -0
  125. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/launcher.py +0 -0
  126. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
  127. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  128. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  129. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
  130. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  131. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
  132. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  133. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  134. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  135. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/preferences.py +0 -0
  136. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
  137. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
  138. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
  139. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/secrets.py +0 -0
  140. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/self_update.py +0 -0
  141. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
  142. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
  143. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/meshcode/upload.py +0 -0
  144. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/scripts/sentinel.py +0 -0
  145. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_core.py +0 -0
  146. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
  147. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
  148. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
  149. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
  150. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
  151. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
  152. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
  153. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
  154. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
  155. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
  156. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
  157. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/comms_v4.py +0 -0
  158. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
  159. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
  160. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/cli.py +0 -0
  161. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
  162. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/compat.py +0 -0
  163. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
  164. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
  165. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/invites.py +0 -0
  166. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
  167. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
  168. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  169. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  170. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
  171. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  172. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
  173. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  174. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  175. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  176. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
  177. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
  178. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
  179. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
  180. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
  181. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
  182. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
  183. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
  184. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/meshcode/upload.py +0 -0
  185. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
  186. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_core.py +0 -0
  187. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
  188. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
  189. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
  190. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
  191. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
  192. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
  193. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
  194. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
  195. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
  196. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
  197. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
  198. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode.egg-info/dependency_links.txt +0 -0
  199. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode.egg-info/entry_points.txt +0 -0
  200. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode.egg-info/requires.txt +0 -0
  201. {meshcode-2.11.9 → meshcode-2.11.11}/meshcode.egg-info/top_level.txt +0 -0
  202. {meshcode-2.11.9 → meshcode-2.11.11}/setup.cfg +0 -0
  203. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_auto_update_hardening.py +0 -0
  204. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_boot_bug_regression.py +0 -0
  205. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_core.py +0 -0
  206. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_cross_agent_messaging.py +0 -0
  207. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_epistemic_v1_python_sdk.py +0 -0
  208. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  209. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_esc_deaf_state.py +0 -0
  210. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_exceptions.py +0 -0
  211. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_lease_sigterm_release.py +0 -0
  212. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_mark_read_batch.py +0 -0
  213. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_migration_integrity.py +0 -0
  214. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_realtime_event_freshness.py +0 -0
  215. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_rls_cross_tenant.py +0 -0
  216. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_rpc_migrations.py +0 -0
  217. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_security_regressions.py +0 -0
  218. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_sentinel.py +0 -0
  219. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_sleep_signals.py +0 -0
  220. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_status_enum_coverage.py +0 -0
  221. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_stay_on_loop_hook.py +0 -0
  222. {meshcode-2.11.9 → meshcode-2.11.11}/tests/test_wait_open_tasks_contradiction.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.9
3
+ Version: 2.11.11
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.11.9"
2
+ __version__ = "2.11.11"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -0,0 +1,42 @@
1
+ """Helpful error when user invokes `python -m meshcode` because the
2
+ console-script isn't on PATH yet (B5 friction).
3
+
4
+ Also dispatches `python -m meshcode setup-path` to the real CLI so the
5
+ user can fix PATH without needing the `meshcode` binary on PATH first.
6
+ """
7
+ import os
8
+ import sys
9
+
10
+
11
+ def main():
12
+ # If the user explicitly asked to set up PATH, delegate to the real CLI.
13
+ if len(sys.argv) > 1 and sys.argv[1] == "setup-path":
14
+ from meshcode import comms_v4 as _cv # noqa: F401
15
+ sys.argv[0] = "meshcode"
16
+ # comms_v4 runs main() at module load via `if __name__ == "__main__"`.
17
+ # Re-exec the body by importing as __main__ would require executing
18
+ # the module again; simpler: invoke via runpy.
19
+ import runpy
20
+ runpy.run_module("meshcode.comms_v4", run_name="__main__", alter_sys=True)
21
+ return
22
+
23
+ # Otherwise: the user is here because `meshcode` isn't on PATH.
24
+ print()
25
+ print(" meshcode is installed but the `meshcode` command isn't on your PATH.")
26
+ print()
27
+ print(" Quick fix:")
28
+ print(f" {sys.executable} -m meshcode setup-path")
29
+ print()
30
+ print(" Or manually:")
31
+ v = sys.version_info
32
+ if sys.platform == "darwin":
33
+ print(f' export PATH="$HOME/Library/Python/{v.major}.{v.minor}/bin:$PATH"')
34
+ else:
35
+ print(' export PATH="$(python3 -m site --user-base)/bin:$PATH"')
36
+ print()
37
+ print(" Then re-run: meshcode --help")
38
+ sys.exit(1)
39
+
40
+
41
+ if __name__ == "__main__":
42
+ main()
@@ -2143,6 +2143,30 @@ def cmd_doctor(flags, pos):
2143
2143
  except Exception as e:
2144
2144
  fail("MCP server", f"import error: {e}. Run: pip install --upgrade meshcode")
2145
2145
 
2146
+ # 4d. Env checks (B2 + B9): Python stub, SSL, PATH, Claude, Node, git, prefs
2147
+ try:
2148
+ from meshcode.doctor import CHECKS as _env_checks
2149
+ # Skip ones cmd_doctor already covered (python version, api key)
2150
+ from meshcode.doctor import (
2151
+ _check_python,
2152
+ _check_api_key,
2153
+ _check_meshcode_on_path, # cmd_doctor doesn't check this
2154
+ )
2155
+ _seen = set()
2156
+ for _fn in _env_checks:
2157
+ if _fn in (_check_python, _check_api_key):
2158
+ continue
2159
+ try:
2160
+ _lvl, _title, _detail = _fn()
2161
+ if _title in _seen:
2162
+ continue
2163
+ _seen.add(_title)
2164
+ {"ok": ok, "warn": warn, "fail": fail}[_lvl](_title, _detail)
2165
+ except Exception as _e:
2166
+ warn(_fn.__name__.lstrip("_"), f"check crashed: {_e}")
2167
+ except ImportError:
2168
+ pass # doctor module not available (legacy install)
2169
+
2146
2170
  # 5. Stale agents check (requires auth)
2147
2171
  if api_key:
2148
2172
  try:
@@ -2745,8 +2769,33 @@ if __name__ == "__main__":
2745
2769
  print(" " + "=" * 40)
2746
2770
  print()
2747
2771
 
2748
- # Try device-code pairing first
2749
- pairing = sb_rpc("mc_create_pairing_session", {})
2772
+ # B2 pre-flight: refuse to onboard if env is broken
2773
+ from meshcode.doctor import run_doctor
2774
+ if run_doctor(skip_api_key_check=True) != 0:
2775
+ print()
2776
+ print(" [meshcode] Fix the blocker(s) above, then run `meshcode init` again.")
2777
+ sys.exit(1)
2778
+ print()
2779
+
2780
+ # B3: wrap pairing RPC so SSL / network errors abort cleanly (no
2781
+ # silent fall-through to API-key paste — friction #7).
2782
+ try:
2783
+ pairing = sb_rpc("mc_create_pairing_session", {})
2784
+ except Exception as _init_e:
2785
+ _msg = str(_init_e).lower()
2786
+ if "certificate" in _msg or "ssl" in _msg:
2787
+ print()
2788
+ print(" [meshcode] SSL certificate verification failed.")
2789
+ print(" [meshcode] This usually means your Python install is missing root certs.")
2790
+ _v = sys.version_info
2791
+ print(f" [meshcode] Fix: run /Applications/Python\\ {_v.major}.{_v.minor}/Install\\ Certificates.command")
2792
+ print(f" [meshcode] (Replace {_v.major}.{_v.minor} with your actual installed version.)")
2793
+ print(f" [meshcode] Alternative: pip install --upgrade certifi")
2794
+ else:
2795
+ print()
2796
+ print(f" [meshcode] Pairing service unreachable: {_init_e}")
2797
+ print(f" [meshcode] Check your network, then re-run `meshcode init`.")
2798
+ sys.exit(1)
2750
2799
  if pairing and isinstance(pairing, dict) and pairing.get("ok"):
2751
2800
  session_id = pairing["session_id"]
2752
2801
  code = pairing["code"]
@@ -2819,7 +2868,7 @@ if __name__ == "__main__":
2819
2868
  # doctor, help, version, login, prefs, launcher don't need auth.
2820
2869
  _NO_AUTH_CMDS = {"doctor", "compat", "upgrade", "help", "--help", "-h", "login",
2821
2870
  "init", "prefs", "launcher", "--version", "-V", "version",
2822
- "whoami", "profiles", "scan"}
2871
+ "whoami", "profiles", "scan", "setup-path"}
2823
2872
  if cmd not in _NO_AUTH_CMDS:
2824
2873
  _cli_key = _load_api_key_for_cli()
2825
2874
  if not _cli_key:
@@ -3430,6 +3479,69 @@ if __name__ == "__main__":
3430
3479
  print(" meshcode prefs reset")
3431
3480
  sys.exit(1)
3432
3481
 
3482
+ elif cmd == "setup-path":
3483
+ # B5: append Python user-bin to user's shell rc file (idempotent).
3484
+ # Friction #8 fix: after `pip install --user meshcode`, `meshcode`
3485
+ # isn't on PATH; this gives the user a one-command remediation.
3486
+ import platform as _pl
3487
+ import subprocess as _sp
3488
+ _v = sys.version_info
3489
+ if _pl.system() == "Darwin":
3490
+ user_bin = os.path.expanduser(f"~/Library/Python/{_v.major}.{_v.minor}/bin")
3491
+ else:
3492
+ try:
3493
+ user_bin = _sp.check_output(
3494
+ [sys.executable, "-m", "site", "--user-base"],
3495
+ text=True, timeout=5,
3496
+ ).strip() + "/bin"
3497
+ except Exception:
3498
+ user_bin = os.path.expanduser("~/.local/bin")
3499
+
3500
+ shell = os.environ.get("SHELL", "")
3501
+ if "zsh" in shell:
3502
+ rc = os.path.expanduser("~/.zshrc")
3503
+ fish = False
3504
+ elif "fish" in shell:
3505
+ rc = os.path.expanduser("~/.config/fish/config.fish")
3506
+ fish = True
3507
+ else:
3508
+ rc = os.path.expanduser("~/.bashrc")
3509
+ fish = False
3510
+
3511
+ marker = "# meshcode: PATH for user-installed Python binaries"
3512
+ line = (f"set -gx PATH {user_bin} $PATH" if fish
3513
+ else f'export PATH="{user_bin}:$PATH"')
3514
+
3515
+ existing = ""
3516
+ try:
3517
+ if os.path.exists(rc):
3518
+ with open(rc) as f:
3519
+ existing = f.read()
3520
+ except Exception:
3521
+ pass
3522
+
3523
+ if user_bin in existing:
3524
+ print(f"[meshcode] {rc} already exports {user_bin} — nothing to do.")
3525
+ sys.exit(0)
3526
+
3527
+ try:
3528
+ os.makedirs(os.path.dirname(rc), exist_ok=True)
3529
+ with open(rc, "a") as f:
3530
+ f.write(f"\n{marker}\n{line}\n")
3531
+ print(f"[meshcode] Appended PATH export to {rc}")
3532
+ print(f"[meshcode] Open a new terminal OR run: source {rc}")
3533
+ print(f"[meshcode] Then: meshcode --help")
3534
+ sys.exit(0)
3535
+ except PermissionError:
3536
+ print(f"[meshcode] ERROR: cannot write {rc} (permission denied).")
3537
+ print(f"[meshcode] Manually add this line to your shell rc:")
3538
+ print(f" {line}")
3539
+ sys.exit(1)
3540
+ except Exception as _spe:
3541
+ print(f"[meshcode] ERROR: {_spe}")
3542
+ print(f"[meshcode] Manually add: {line}")
3543
+ sys.exit(1)
3544
+
3433
3545
  elif cmd == "upgrade":
3434
3546
  # meshcode upgrade — interactive foreground upgrade
3435
3547
  import importlib
@@ -0,0 +1,277 @@
1
+ """Pre-flight environment checks. `meshcode doctor` runs all and reports.
2
+
3
+ Each check returns (level, title, detail):
4
+ level: "ok" | "warn" | "fail"
5
+ title: short identifier shown to user
6
+ detail: actionable fix when not OK ("" when OK)
7
+
8
+ Exit code: 1 if any "fail", else 0 (warnings allowed).
9
+
10
+ Covers frictions #4 Python stub, #5 SSL certs, #8 PATH, #10 git/CLT modal,
11
+ #11 Claude not installed. Used as pre-flight by `meshcode init`.
12
+ """
13
+ import os
14
+ import sys
15
+ import json
16
+ import shutil
17
+ import subprocess
18
+ import urllib.request
19
+ import urllib.error
20
+ from typing import List, Tuple
21
+
22
+ Status = Tuple[str, str, str]
23
+
24
+ _HEALTH_URL = "https://meshcode.io/health"
25
+ _SUPABASE_URL = "https://supabase.co"
26
+
27
+
28
+ def _check_python() -> Status:
29
+ v = sys.version_info
30
+ if (v.major, v.minor) < (3, 9):
31
+ return (
32
+ "fail",
33
+ f"Python {v.major}.{v.minor}.{v.micro}",
34
+ "need Python >=3.9; install from python.org or `brew install python@3.13`",
35
+ )
36
+ # macOS Apple-stub detection: importing ssl on the stub fails
37
+ if sys.platform == "darwin":
38
+ try:
39
+ import ssl
40
+
41
+ _ = ssl.OPENSSL_VERSION
42
+ except Exception as e:
43
+ return (
44
+ "fail",
45
+ f"Python {v.major}.{v.minor}.{v.micro}",
46
+ f"Apple stub Python detected (ssl import failed: {e}). "
47
+ f"Install real Python: https://www.python.org/downloads/",
48
+ )
49
+ return ("ok", f"Python {v.major}.{v.minor}.{v.micro}", "")
50
+
51
+
52
+ def _check_ssl() -> Status:
53
+ try:
54
+ urllib.request.urlopen(_HEALTH_URL, timeout=5).read(64)
55
+ return ("ok", "SSL certs", "")
56
+ except urllib.error.URLError as e:
57
+ msg = str(e).lower()
58
+ if "certificate" in msg or "ssl" in msg:
59
+ fix = (
60
+ "run python's Install Certificates.command "
61
+ "(/Applications/Python <ver>/Install Certificates.command) "
62
+ "OR `pip install --upgrade certifi`"
63
+ )
64
+ return ("fail", "SSL certs", fix)
65
+ return ("warn", "SSL certs", f"network unreachable (skip if offline): {e}")
66
+ except Exception as e:
67
+ return ("warn", "SSL certs", f"check inconclusive: {e}")
68
+
69
+
70
+ def _check_meshcode_on_path() -> Status:
71
+ for tool in ("meshcode", "meshcode-mcp"):
72
+ if not shutil.which(tool):
73
+ v = sys.version_info
74
+ if sys.platform == "darwin":
75
+ hint = (
76
+ f'export PATH="$HOME/Library/Python/{v.major}.{v.minor}/bin:$PATH" '
77
+ f'(or run: python3 -m meshcode setup-path)'
78
+ )
79
+ else:
80
+ hint = (
81
+ "add Python user-base bin to PATH "
82
+ "(python3 -m site --user-base)/bin "
83
+ "or run: python3 -m meshcode setup-path"
84
+ )
85
+ return ("fail", f"`{tool}` on PATH", hint)
86
+ return ("ok", "meshcode CLI on PATH", "")
87
+
88
+
89
+ def _check_claude() -> Status:
90
+ if shutil.which("claude"):
91
+ return ("ok", "Claude Code installed", "")
92
+ return (
93
+ "fail",
94
+ "Claude Code installed",
95
+ "install from https://claude.ai/install "
96
+ "or `npm i -g @anthropic-ai/claude-code`",
97
+ )
98
+
99
+
100
+ def _check_node() -> Status:
101
+ needs_node = False
102
+ try:
103
+ from meshcode.preferences import load_prefs
104
+
105
+ cv = (load_prefs().get("claude_version") or "").lower()
106
+ if cv and cv not in ("latest", "none", "skip"):
107
+ needs_node = True
108
+ except Exception:
109
+ pass
110
+ if os.environ.get("MESHCODE_USE_NPX"):
111
+ needs_node = True
112
+ has_node = bool(shutil.which("node"))
113
+ has_npx = bool(shutil.which("npx"))
114
+ if has_node and has_npx:
115
+ return ("ok", "Node/npx", "")
116
+ level = "fail" if needs_node else "warn"
117
+ detail = (
118
+ "install Node: https://nodejs.org or `brew install node`"
119
+ if needs_node
120
+ else "only needed for `claude_version` pin or MESHCODE_USE_NPX; safe to skip"
121
+ )
122
+ return (level, "Node/npx", detail)
123
+
124
+
125
+ def _check_git() -> Status:
126
+ """git pre-flight with Apple stub detection + safe workarounds (B9)."""
127
+ git = shutil.which("git")
128
+ if not git:
129
+ return (
130
+ "fail",
131
+ "git",
132
+ "git not installed. Recommended (skip Apple's brittle modal):\n"
133
+ " - Manual CLT download: https://developer.apple.com/download/all/?q=command%20line%20tools\n"
134
+ " - OR Git for Mac: https://git-scm.com/download/mac\n"
135
+ " - OR `brew install git` (if Homebrew is installed)",
136
+ )
137
+ if sys.platform == "darwin":
138
+ try:
139
+ r = subprocess.run(
140
+ [git, "--version"],
141
+ capture_output=True,
142
+ text=True,
143
+ timeout=3,
144
+ )
145
+ stderr_lower = (r.stderr or "").lower()
146
+ if (
147
+ r.returncode != 0
148
+ or "command line developer tools" in stderr_lower
149
+ ):
150
+ return (
151
+ "fail",
152
+ "git",
153
+ "Apple stub git detected (would trigger CLT install modal). "
154
+ "Install real git: "
155
+ "https://developer.apple.com/download/all/?q=command%20line%20tools "
156
+ "OR `brew install git` OR https://git-scm.com/download/mac",
157
+ )
158
+ except subprocess.TimeoutExpired:
159
+ return (
160
+ "fail",
161
+ "git",
162
+ "git subprocess hung — likely the CLT install modal is "
163
+ "blocked or pending. Cancel any open Apple dialog; install "
164
+ "git manually: https://git-scm.com/download/mac",
165
+ )
166
+ except Exception:
167
+ pass
168
+ return ("ok", "git", "")
169
+
170
+
171
+ def _check_network() -> Status:
172
+ for url in (_HEALTH_URL, _SUPABASE_URL):
173
+ try:
174
+ urllib.request.urlopen(url, timeout=5).read(64)
175
+ except Exception as e:
176
+ return (
177
+ "warn",
178
+ "Network reachability",
179
+ f"{url} unreachable ({type(e).__name__}); "
180
+ f"check VPN/firewall if persistent",
181
+ )
182
+ return ("ok", "Network reachability", "")
183
+
184
+
185
+ def _check_api_key() -> Status:
186
+ try:
187
+ from meshcode.secrets import get_api_key, list_profiles
188
+
189
+ profiles = list_profiles() or []
190
+ if not profiles:
191
+ return (
192
+ "warn",
193
+ "API key in keychain",
194
+ "no profiles — run `meshcode init` to pair and store one",
195
+ )
196
+ for p in profiles:
197
+ if get_api_key(profile=p):
198
+ return ("ok", f"API key (profile={p})", "")
199
+ return (
200
+ "warn",
201
+ "API key in keychain",
202
+ f"profiles exist ({profiles}) but no key retrievable — "
203
+ f"`meshcode init` to re-pair",
204
+ )
205
+ except Exception as e:
206
+ return ("warn", "API key in keychain", f"secrets module unavailable: {e}")
207
+
208
+
209
+ def _check_prefs_json() -> Status:
210
+ pref = os.path.expanduser("~/.meshcode/preferences.json")
211
+ if not os.path.exists(pref):
212
+ return ("ok", "preferences.json", "(not created yet — default fine)")
213
+ try:
214
+ with open(pref) as f:
215
+ json.load(f)
216
+ return ("ok", "preferences.json", "")
217
+ except json.JSONDecodeError as e:
218
+ return (
219
+ "fail",
220
+ "preferences.json",
221
+ f"corrupt JSON ({e}); delete or fix: {pref}",
222
+ )
223
+ except Exception as e:
224
+ return ("warn", "preferences.json", f"unreadable: {e}")
225
+
226
+
227
+ CHECKS = [
228
+ _check_python,
229
+ _check_ssl,
230
+ _check_meshcode_on_path,
231
+ _check_claude,
232
+ _check_node,
233
+ _check_git,
234
+ _check_network,
235
+ _check_api_key,
236
+ _check_prefs_json,
237
+ ]
238
+
239
+
240
+ def run_doctor(skip_api_key_check: bool = False, quiet: bool = False) -> int:
241
+ """Run all checks, print results, return exit code (0 ok/warn, 1 fail)."""
242
+ results: List[Status] = []
243
+ for fn in CHECKS:
244
+ if skip_api_key_check and fn is _check_api_key:
245
+ continue
246
+ try:
247
+ results.append(fn())
248
+ except Exception as e:
249
+ results.append(("fail", fn.__name__.lstrip("_"), f"check crashed: {e}"))
250
+ blockers = 0
251
+ if not quiet:
252
+ print()
253
+ print(" meshcode doctor — environment check")
254
+ print(" " + "=" * 40)
255
+ for level, title, detail in results:
256
+ sym = {"ok": "✓", "warn": "⚠", "fail": "✗"}[level]
257
+ if level == "fail":
258
+ blockers += 1
259
+ line = f" {sym} {title}"
260
+ if detail:
261
+ line += f" — {detail}"
262
+ if not quiet:
263
+ print(line)
264
+ if not quiet:
265
+ print()
266
+ if blockers:
267
+ print(
268
+ f" {blockers} blocker(s) found — fix the above "
269
+ f"before `meshcode init`/`go`."
270
+ )
271
+ else:
272
+ print(" All checks passed (warnings non-blocking).")
273
+ return 1 if blockers else 0
274
+
275
+
276
+ if __name__ == "__main__":
277
+ sys.exit(run_doctor())
@@ -45,9 +45,21 @@ _ANSI_RGB = [
45
45
  (255, 85, 85), (85, 255, 255), (255, 85, 255), (85, 255, 85), (85, 85, 255),
46
46
  ]
47
47
 
48
+ # BUG-AUDIO-COLOR fix (task e552412a, 2026-05-19): use 24-bit truecolor
49
+ # escapes when the terminal supports them so every terminal renders the
50
+ # same agent color (4-bit palette indices are remapped by terminal themes,
51
+ # producing the cross-machine mismatch Samuel saw).
52
+ _TRUECOLOR_OK = os.environ.get("COLORTERM", "").lower() in ("truecolor", "24bit")
53
+
48
54
 
49
55
  def _hex_to_ansi(hex_color: str) -> str:
50
- """Map hex color to nearest ANSI code."""
56
+ """Return ANSI escape for a hex color.
57
+
58
+ On truecolor-capable terminals returns 24-bit truecolor (\\e[38;2;R;G;Bm) so
59
+ the color bypasses palette remapping. Falls back to nearest 4-bit ANSI
60
+ palette index when COLORTERM is not truecolor.
61
+ Returns None if hex is malformed.
62
+ """
51
63
  hx = hex_color.strip().lstrip("#")
52
64
  if len(hx) != 6:
53
65
  return None
@@ -55,6 +67,9 @@ def _hex_to_ansi(hex_color: str) -> str:
55
67
  r, g, b = int(hx[0:2], 16), int(hx[2:4], 16), int(hx[4:6], 16)
56
68
  except ValueError:
57
69
  return None
70
+ if _TRUECOLOR_OK:
71
+ return f"\033[38;2;{r};{g};{b}m"
72
+ # Fallback: map to nearest 4-bit ANSI palette index
58
73
  best_i, best_d = 0, float("inf")
59
74
  for i, (ar, ag, ab) in enumerate(_ANSI_RGB):
60
75
  d = (r - ar) ** 2 + (g - ag) ** 2 + (b - ab) ** 2
@@ -65,10 +80,18 @@ def _hex_to_ansi(hex_color: str) -> str:
65
80
 
66
81
 
67
82
  def _agent_color(name: str) -> str:
68
- """ANSI color from profile (dashboard) or MD5 hash fallback."""
83
+ """Profile-canonical color if cached, else a deterministic per-name color.
84
+ Never random — same name -> same color on every machine."""
69
85
  if _CACHED_AGENT_COLOR:
70
86
  return _CACHED_AGENT_COLOR
71
87
  h = int(_hashlib.md5(name.encode()).hexdigest()[:8], 16)
88
+ if _TRUECOLOR_OK:
89
+ # Pseudo-random but deterministic 24-bit color from the hash. Clamp
90
+ # mid-range so the color is readable on both dark and light terminals.
91
+ r = max(70, min(220, (h >> 16) & 0xFF))
92
+ g = max(70, min(220, (h >> 8) & 0xFF))
93
+ b = max(70, min(220, h & 0xFF))
94
+ return f"\033[38;2;{r};{g};{b}m"
72
95
  return _ANSI_COLORS[h % len(_ANSI_COLORS)]
73
96
 
74
97
 
@@ -461,6 +461,19 @@ def _resolve_pinned_claude(editor_cmd: str) -> str:
461
461
  """
462
462
  from .preferences import load_prefs
463
463
 
464
+ # B4: if user opts into legacy npx path AND no version is specified,
465
+ # fall back to the historical ESC-safe pin (2.1.104). Otherwise default
466
+ # to using installed claude — no implicit Node dependency.
467
+ if (
468
+ os.environ.get("MESHCODE_USE_NPX")
469
+ and not os.environ.get("MESHCODE_CLAUDE_VERSION")
470
+ ):
471
+ os.environ["MESHCODE_CLAUDE_VERSION"] = "2.1.104"
472
+ print(
473
+ f"[meshcode] --use-npx requested; pinning to legacy ESC-safe 2.1.104",
474
+ file=sys.stderr,
475
+ )
476
+
464
477
  pinned = os.environ.get("MESHCODE_CLAUDE_VERSION", "").strip()
465
478
  if not pinned:
466
479
  # Check global admin config from DB
@@ -471,19 +484,24 @@ def _resolve_pinned_claude(editor_cmd: str) -> str:
471
484
  if not pinned:
472
485
  pinned = load_prefs().get("claude_version", "")
473
486
 
474
- # "latest" or "none" means skip pinning use whatever is installed
487
+ # "latest"/"none"/"skip" explicit opt-out of any pin
475
488
  if pinned and pinned.lower() in ("latest", "none", "skip"):
476
489
  print(f"[meshcode] Version pin disabled ({pinned}) — using installed version", file=sys.stderr)
477
490
  return editor_cmd
478
491
 
492
+ # B4: default = use installed claude. No implicit Node/npx tax. Users who
493
+ # want a specific version pin must opt in via:
494
+ # - `meshcode prefs claude-version <ver>` (persistent)
495
+ # - MESHCODE_CLAUDE_VERSION env var (one-shot)
496
+ # - --use-npx flag on `meshcode go` (one-shot, sets env)
497
+ #
498
+ # The 2.1.104 ESC-safe pin used to be the default; it added a hard Node
499
+ # dependency for users who didn't expect one. The ESC-cancel bug it works
500
+ # around should be addressed in meshcode_mcp/server.py directly; until
501
+ # then, --use-npx remains the escape hatch.
479
502
  if not pinned:
480
- # Default pin: v2.1.104 is the last Claude Code version where ESC
481
- # doesn't kill the MCP server. Newer versions send CancelledError
482
- # that cascades through the event loop. This default is overridden
483
- # by any of the 3 pin sources above (set to "latest" to disable).
484
- # Remove this default once Anthropic fixes the ESC bug upstream.
485
- pinned = "2.1.104"
486
- print(f"[meshcode] Default ESC-safe pin: Claude Code v{pinned}", file=sys.stderr)
503
+ print(f"[meshcode] Using installed Claude Code (no version pin).", file=sys.stderr)
504
+ return editor_cmd
487
505
 
488
506
  current = _get_claude_version(editor_cmd)
489
507
  if current == pinned:
@@ -792,6 +810,52 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
792
810
  print("[meshcode] Then verify: which claude", file=sys.stderr)
793
811
  return 2
794
812
 
813
+ # B9 pre-flight: warn early if git is missing or is the Apple stub.
814
+ # We don't block boot — many agents work without git — but surface the
815
+ # friction so the user isn't surprised by an Apple modal when the
816
+ # Claude Code shell tool first runs `git status`.
817
+ if sys.platform == "darwin":
818
+ try:
819
+ import shutil as _shutil
820
+ _git = _shutil.which("git")
821
+ if not _git:
822
+ print(
823
+ "[meshcode] WARNING: git not found on PATH. Claude Code may "
824
+ "trigger the macOS 'install developer tools' modal when it "
825
+ "first runs git. Install manually: "
826
+ "https://git-scm.com/download/mac or `brew install git`",
827
+ file=sys.stderr,
828
+ )
829
+ else:
830
+ _r = subprocess.run(
831
+ [_git, "--version"],
832
+ capture_output=True,
833
+ text=True,
834
+ timeout=3,
835
+ )
836
+ if (
837
+ _r.returncode != 0
838
+ or "command line developer tools" in (_r.stderr or "").lower()
839
+ ):
840
+ print(
841
+ "[meshcode] WARNING: Apple stub git detected. "
842
+ "Real git: https://git-scm.com/download/mac or "
843
+ "`brew install git`",
844
+ file=sys.stderr,
845
+ )
846
+ except Exception:
847
+ pass # Soft-fail — boot must proceed regardless.
848
+
849
+ # BUG-AUDIO-COLOR fix: wait briefly for profile fetch to populate
850
+ # _CACHED_AGENT_COLOR before rendering the banner, so the boot art
851
+ # doesn't flash the deterministic-hash fallback color and then "jump"
852
+ # to the canonical profile color. Upper-bound 350ms.
853
+ try:
854
+ from .meshcode_mcp.server import _REGISTER_PROFILE_READY
855
+ _REGISTER_PROFILE_READY.wait(timeout=0.35)
856
+ except Exception:
857
+ pass
858
+
795
859
  # ── Welcome banner with unique ASCII art + personality ──────────
796
860
  try:
797
861
  from .ascii_art import generate_art, render_welcome
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.9
3
+ Version: 2.11.11
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -1,12 +1,14 @@
1
1
  README.md
2
2
  pyproject.toml
3
3
  meshcode/__init__.py
4
+ meshcode/__main__.py
4
5
  meshcode/_stop_hook_template.py
5
6
  meshcode/ascii_art.py
6
7
  meshcode/cli.py
7
8
  meshcode/comms_v4.py
8
9
  meshcode/compat.py
9
10
  meshcode/daemon.py
11
+ meshcode/doctor.py
10
12
  meshcode/error_hints.py
11
13
  meshcode/exceptions.py
12
14
  meshcode/invites.py
@@ -194,8 +196,10 @@ meshcode/scripts/check_secrets.py
194
196
  meshcode/scripts/race_rate_harness.py
195
197
  tests/test_auto_update_hardening.py
196
198
  tests/test_boot_bug_regression.py
199
+ tests/test_color_truecolor.py
197
200
  tests/test_core.py
198
201
  tests/test_cross_agent_messaging.py
202
+ tests/test_doctor.py
199
203
  tests/test_epistemic_v1_python_sdk.py
200
204
  tests/test_epistemic_v1_stop_conditions.py
201
205
  tests/test_esc_deaf_state.py
@@ -209,6 +213,7 @@ tests/test_rls_cross_tenant.py
209
213
  tests/test_rpc_migrations.py
210
214
  tests/test_security_regressions.py
211
215
  tests/test_sentinel.py
216
+ tests/test_setup_path.py
212
217
  tests/test_sleep_signals.py
213
218
  tests/test_status_enum_coverage.py
214
219
  tests/test_stay_on_loop_hook.py