mixhub-cli 0.2.0__tar.gz → 0.2.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 (335) hide show
  1. mixhub_cli-0.2.1/.github/workflows/publish.yml +28 -0
  2. mixhub_cli-0.2.1/.github/workflows/test.yml +34 -0
  3. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/.gitignore +3 -0
  4. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/PKG-INFO +92 -51
  5. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/Preferences.example +3 -3
  6. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/README.md +90 -49
  7. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/bin/hub +1 -1
  8. mixhub_cli-0.2.1/mixhub/__init__.py +12 -0
  9. mixhub_cli-0.2.1/mixhub/channels/__init__.py +55 -0
  10. mixhub_cli-0.2.1/mixhub/channels/base.py +35 -0
  11. mixhub_cli-0.2.1/mixhub/channels/cli.py +92 -0
  12. mixhub_cli-0.2.1/mixhub/channels/config.py +165 -0
  13. mixhub_cli-0.2.1/mixhub/channels/hooks.py +92 -0
  14. mixhub_cli-0.2.1/mixhub/channels/manager.py +73 -0
  15. mixhub_cli-0.2.1/mixhub/channels/slack.py +57 -0
  16. mixhub_cli-0.2.1/mixhub/channels/telegram.py +263 -0
  17. mixhub_cli-0.2.1/mixhub/channels/text.py +38 -0
  18. mixhub_cli-0.2.1/mixhub/channels/webhook.py +50 -0
  19. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/cli/__init__.py +2 -2
  20. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/cli/main.py +10 -41
  21. mixhub_cli-0.2.1/mixhub/cli/sessions.py +35 -0
  22. mixhub_cli-0.2.1/mixhub/cli/tools.py +19 -0
  23. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/cli/update.py +2 -1
  24. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/cli/workspace.py +53 -17
  25. mixhub_cli-0.2.1/mixhub/core/__init__.py +78 -0
  26. mixhub_cli-0.2.1/mixhub/core/browser.py +40 -0
  27. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/core/launch.py +9 -11
  28. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/core/paths.py +0 -14
  29. mixhub_cli-0.2.1/mixhub/core/prefs.py +61 -0
  30. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/core/state.py +0 -24
  31. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/core/textwidth.py +14 -10
  32. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/__init__.py +2 -2
  33. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/main.py +6 -13
  34. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/prefs.py +2 -33
  35. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/stream.py +5 -38
  36. mixhub_cli-0.2.1/mixhub/handoff/__init__.py +60 -0
  37. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/handoff/bundle.py +1 -53
  38. mixhub_cli-0.2.1/mixhub/handoff/disposition.py +23 -0
  39. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/handoff/extract.py +0 -13
  40. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/handoff/relay.py +19 -85
  41. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/__init__.py +4 -15
  42. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/actions.py +23 -2
  43. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/discovery.py +98 -7
  44. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/hub.py +2 -0
  45. mixhub_cli-0.2.1/mixhub/hub/integrations.py +100 -0
  46. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/lifecycle.py +27 -176
  47. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/pane_probe.py +10 -0
  48. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/processes.py +35 -1
  49. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/schema.py +4 -0
  50. mixhub_cli-0.2.1/mixhub/hub/send.py +140 -0
  51. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/session_filters.py +15 -12
  52. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/sleep_blockers.py +5 -5
  53. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/summary.py +2 -193
  54. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/util.py +3 -2
  55. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/working_set.py +0 -1
  56. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/i18n/__init__.py +5 -5
  57. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/i18n/en.json +41 -34
  58. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/i18n/ja.json +42 -35
  59. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/i18n/zh.json +50 -43
  60. mixhub_cli-0.2.1/mixhub/ops/__init__.py +8 -0
  61. mixhub_cli-0.2.1/mixhub/ops/doctor.py +119 -0
  62. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/probe/__init__.py +2 -4
  63. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/probe/capture.py +0 -11
  64. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/proc/classify.py +2 -3
  65. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/proc/identity.py +13 -1
  66. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/__init__.py +1 -63
  67. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/claude.py +1 -3
  68. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/jsonl.py +1 -1
  69. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/launch.py +0 -30
  70. mixhub_cli-0.2.1/mixhub/providers/preferences.py +13 -0
  71. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/presets.py +1 -37
  72. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/quota.py +1 -1
  73. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/readiness.py +10 -107
  74. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/title_overrides.py +16 -73
  75. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/rules/__init__.py +0 -1
  76. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/rules/sync.py +1 -1
  77. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/rules/trust.py +5 -38
  78. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/__init__.py +1 -1
  79. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/gateway.py +17 -51
  80. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/new_sessions.py +112 -8
  81. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/onexit.py +1 -1
  82. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/outer.py +20 -18
  83. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/pool.py +27 -8
  84. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/session_state.py +8 -15
  85. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/workspace.py +38 -72
  86. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/__init__.py +1 -4
  87. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/__main__.py +1 -1
  88. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/app.py +20 -54
  89. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/badges.py +1 -47
  90. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/style.py +0 -16
  91. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/__init__.py +2 -2
  92. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/actions.py +10 -221
  93. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/attention.py +15 -13
  94. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/draw.py +0 -9
  95. mixhub_cli-0.2.1/mixhub/tui/hubtui/evq.py +99 -0
  96. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/home.py +43 -23
  97. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/layout.py +8 -21
  98. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/loop.py +30 -190
  99. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/mouse.py +0 -28
  100. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/popovers.py +4 -19
  101. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/reader.py +11 -36
  102. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/relay.py +3 -63
  103. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/selection_sync.py +7 -3
  104. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/sidebar.py +0 -25
  105. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/wordmark_row.py +1 -1
  106. mixhub_cli-0.2.1/mixhub/tui/settings.py +9 -0
  107. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/web.py +2 -17
  108. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/widgets/__init__.py +0 -6
  109. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/widgets/popover.py +2 -51
  110. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/widgets/quota_table.py +0 -51
  111. mixhub_cli-0.2.1/mixhub/tui/widgets/toast.py +17 -0
  112. mixhub_cli-0.2.1/mixhub/tui/widgets/wordmark.py +203 -0
  113. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/web/handler.py +1 -18
  114. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/web/static/app.js +59 -48
  115. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/web/static/index.html +23 -24
  116. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/web/static/style.css +6 -2
  117. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/pyproject.toml +1 -1
  118. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/scripts/demo_seed.py +95 -7
  119. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/agy_claude_filter.py +3 -11
  120. mixhub_cli-0.2.1/tests/compat/channels.py +28 -0
  121. mixhub_cli-0.2.1/tests/compat/handoff.py +56 -0
  122. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/probe.py +1 -9
  123. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/providers.py +0 -67
  124. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/rules_sync.py +2 -10
  125. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/server.py +0 -1
  126. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/tui.py +1 -4
  127. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/wordmark.py +7 -8
  128. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_agy_filter_package.py +2 -3
  129. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_agy_headless_and_hibernate_state.py +4 -26
  130. mixhub_cli-0.2.1/tests/test_agy_headless_continued.py +157 -0
  131. mixhub_cli-0.2.1/tests/test_auto_sleep_pref.py +86 -0
  132. mixhub_cli-0.2.1/tests/test_channels_config.py +108 -0
  133. mixhub_cli-0.2.1/tests/test_consumer_checks.py +31 -0
  134. mixhub_cli-0.2.1/tests/test_demo_seed.py +95 -0
  135. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_e2e_ux_audit.py +499 -402
  136. mixhub_cli-0.2.1/tests/test_first_run.py +52 -0
  137. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_handoff_hold_and_fork.py +0 -331
  138. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_harness_presets.py +8 -203
  139. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_hub.py +170 -429
  140. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_hub_providers_layout.py +2 -2
  141. mixhub_cli-0.2.1/tests/test_hubpool_duplicate_windows.py +74 -0
  142. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_i18n.py +9 -40
  143. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_launch_forms.py +13 -22
  144. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_modular_and_trust.py +9 -36
  145. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_modular_channels_handoff.py +8 -108
  146. mixhub_cli-0.2.1/tests/test_no_agent_file_deletion.py +79 -0
  147. mixhub_cli-0.2.1/tests/test_notify_hooks.py +117 -0
  148. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_oss_desensitize_and_cc_ds.py +32 -320
  149. mixhub_cli-0.2.1/tests/test_own_tmux_server.py +126 -0
  150. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_package_import_rule.py +5 -11
  151. mixhub_cli-0.2.1/tests/test_platform_support.py +307 -0
  152. mixhub_cli-0.2.1/tests/test_preferences_lookup.py +67 -0
  153. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_probe.py +1 -125
  154. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_server.py +44 -0
  155. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_state_root.py +17 -21
  156. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_telegram_bot_api_send.py +2 -4
  157. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_tui_core_primitives.py +12 -10
  158. mixhub_cli-0.2.1/tests/test_windows_compat.py +103 -0
  159. mixhub_cli-0.2.1/tests/test_wordmark.py +87 -0
  160. mixhub_cli-0.2.1/tests/test_working_set_removal_log.py +32 -0
  161. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_zoom_toggle_and_badge.py +22 -0
  162. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/webui_composer.test.mjs +162 -21
  163. mixhub_cli-0.2.0/mixhub/__init__.py +0 -52
  164. mixhub_cli-0.2.0/mixhub/channels/__init__.py +0 -144
  165. mixhub_cli-0.2.0/mixhub/channels/approvals.py +0 -367
  166. mixhub_cli-0.2.0/mixhub/channels/attention.py +0 -814
  167. mixhub_cli-0.2.0/mixhub/channels/base.py +0 -69
  168. mixhub_cli-0.2.0/mixhub/channels/cards.py +0 -525
  169. mixhub_cli-0.2.0/mixhub/channels/cli.py +0 -74
  170. mixhub_cli-0.2.0/mixhub/channels/config.py +0 -196
  171. mixhub_cli-0.2.0/mixhub/channels/discord.py +0 -396
  172. mixhub_cli-0.2.0/mixhub/channels/feishu.py +0 -293
  173. mixhub_cli-0.2.0/mixhub/channels/hooks.py +0 -48
  174. mixhub_cli-0.2.0/mixhub/channels/language.py +0 -77
  175. mixhub_cli-0.2.0/mixhub/channels/manager.py +0 -630
  176. mixhub_cli-0.2.0/mixhub/channels/poller_lock.py +0 -132
  177. mixhub_cli-0.2.0/mixhub/channels/receipts.py +0 -365
  178. mixhub_cli-0.2.0/mixhub/channels/router.py +0 -145
  179. mixhub_cli-0.2.0/mixhub/channels/slack.py +0 -320
  180. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/__init__.py +0 -78
  181. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/__main__.py +0 -12
  182. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/approvals.py +0 -263
  183. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/bridge.py +0 -1079
  184. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/config.py +0 -95
  185. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/daemon.py +0 -165
  186. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/delivery.py +0 -227
  187. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/web_api.py +0 -234
  188. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/websocket.py +0 -198
  189. mixhub_cli-0.2.0/mixhub/channels/store.py +0 -430
  190. mixhub_cli-0.2.0/mixhub/channels/telegram.py +0 -1400
  191. mixhub_cli-0.2.0/mixhub/channels/text.py +0 -256
  192. mixhub_cli-0.2.0/mixhub/cli/sessions.py +0 -93
  193. mixhub_cli-0.2.0/mixhub/cli/tools.py +0 -42
  194. mixhub_cli-0.2.0/mixhub/core/__init__.py +0 -159
  195. mixhub_cli-0.2.0/mixhub/core/adapters.py +0 -145
  196. mixhub_cli-0.2.0/mixhub/core/models.py +0 -106
  197. mixhub_cli-0.2.0/mixhub/core/prefs.py +0 -86
  198. mixhub_cli-0.2.0/mixhub/core/protocols.py +0 -122
  199. mixhub_cli-0.2.0/mixhub/core/registry.py +0 -265
  200. mixhub_cli-0.2.0/mixhub/handoff/__init__.py +0 -257
  201. mixhub_cli-0.2.0/mixhub/handoff/branch.py +0 -40
  202. mixhub_cli-0.2.0/mixhub/handoff/common.py +0 -308
  203. mixhub_cli-0.2.0/mixhub/handoff/disposition.py +0 -83
  204. mixhub_cli-0.2.0/mixhub/handoff/fence.py +0 -84
  205. mixhub_cli-0.2.0/mixhub/handoff/fork.py +0 -522
  206. mixhub_cli-0.2.0/mixhub/handoff/holdqueue.py +0 -497
  207. mixhub_cli-0.2.0/mixhub/handoff/receipt.py +0 -68
  208. mixhub_cli-0.2.0/mixhub/handoff/store.py +0 -286
  209. mixhub_cli-0.2.0/mixhub/hub/integrations.py +0 -351
  210. mixhub_cli-0.2.0/mixhub/ops/__init__.py +0 -8
  211. mixhub_cli-0.2.0/mixhub/ops/cost_report.py +0 -629
  212. mixhub_cli-0.2.0/mixhub/ops/doctor.py +0 -27
  213. mixhub_cli-0.2.0/mixhub/ops/icons_font.py +0 -218
  214. mixhub_cli-0.2.0/mixhub/probe/away.py +0 -218
  215. mixhub_cli-0.2.0/mixhub/providers/adapters.py +0 -139
  216. mixhub_cli-0.2.0/mixhub/providers/grok.py +0 -283
  217. mixhub_cli-0.2.0/mixhub/providers/preferences.py +0 -37
  218. mixhub_cli-0.2.0/mixhub/quirks/__init__.py +0 -118
  219. mixhub_cli-0.2.0/mixhub/rules/titles.py +0 -28
  220. mixhub_cli-0.2.0/mixhub/surfaces/__init__.py +0 -71
  221. mixhub_cli-0.2.0/mixhub/tui/onboarding.py +0 -118
  222. mixhub_cli-0.2.0/mixhub/tui/settings.py +0 -34
  223. mixhub_cli-0.2.0/mixhub/tui/widgets/toast.py +0 -21
  224. mixhub_cli-0.2.0/mixhub/tui/widgets/welcome.py +0 -7
  225. mixhub_cli-0.2.0/mixhub/tui/widgets/wordmark.py +0 -348
  226. mixhub_cli-0.2.0/tests/compat/channels.py +0 -129
  227. mixhub_cli-0.2.0/tests/compat/cost_report.py +0 -26
  228. mixhub_cli-0.2.0/tests/compat/handoff.py +0 -126
  229. mixhub_cli-0.2.0/tests/compat/icons_font.py +0 -24
  230. mixhub_cli-0.2.0/tests/compat/protocols.py +0 -59
  231. mixhub_cli-0.2.0/tests/compat/slack_bridge.py +0 -62
  232. mixhub_cli-0.2.0/tests/test_anti_context_rot_primitives.py +0 -289
  233. mixhub_cli-0.2.0/tests/test_away_receipt_zero_emoji.py +0 -1054
  234. mixhub_cli-0.2.0/tests/test_demo_seed.py +0 -49
  235. mixhub_cli-0.2.0/tests/test_handoff_ab.py +0 -99
  236. mixhub_cli-0.2.0/tests/test_platform_support.py +0 -159
  237. mixhub_cli-0.2.0/tests/test_slack_bridge.py +0 -940
  238. mixhub_cli-0.2.0/tests/test_toml_fallback.py +0 -34
  239. mixhub_cli-0.2.0/tests/test_windows_compat.py +0 -247
  240. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/.github/assets/web-dark.png +0 -0
  241. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/.github/assets/web-light.png +0 -0
  242. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/LICENSE +0 -0
  243. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/bin/mixhub +0 -0
  244. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/install.sh +0 -0
  245. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/__main__.py +0 -0
  246. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/cli/daemon.py +0 -0
  247. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/core/env.py +0 -0
  248. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/__init__.py +0 -0
  249. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/__main__.py +0 -0
  250. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/cursor.py +0 -0
  251. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/debuglog.py +0 -0
  252. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/mouse.py +0 -0
  253. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/patterns.py +0 -0
  254. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/filters/agy_claude/vscreen.py +0 -0
  255. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/handoff/contract.py +0 -0
  256. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/handoff/resume.py +0 -0
  257. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/handoff/text.py +0 -0
  258. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/constants.py +0 -0
  259. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/index.py +0 -0
  260. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/listing.py +0 -0
  261. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/metrics.py +0 -0
  262. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/opencode.py +0 -0
  263. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/shell.py +0 -0
  264. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/text.py +0 -0
  265. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/titles.py +0 -0
  266. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/hub/transcript.py +0 -0
  267. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/probe/classify.py +0 -0
  268. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/probe/menus.py +0 -0
  269. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/probe/patterns.py +0 -0
  270. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/probe/rules.py +0 -0
  271. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/probe/text.py +0 -0
  272. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/proc/__init__.py +0 -0
  273. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/proc/guard.py +0 -0
  274. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/proc/tree.py +0 -0
  275. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/deepseek.py +0 -0
  276. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/native_titles.py +0 -0
  277. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/resume.py +0 -0
  278. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/providers/statusline.py +0 -0
  279. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/rules/blocks.py +0 -0
  280. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/commands.py +0 -0
  281. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/headers.py +0 -0
  282. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tmux/home_shell.py +0 -0
  283. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/caffeinate.py +0 -0
  284. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/__init__.py +0 -0
  285. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/canvas.py +0 -0
  286. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/events.py +0 -0
  287. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/geometry.py +0 -0
  288. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/hitmap.py +0 -0
  289. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/input.py +0 -0
  290. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/loop.py +0 -0
  291. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/core/widget.py +0 -0
  292. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/diag.py +0 -0
  293. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/drafts.py +0 -0
  294. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/colors.py +0 -0
  295. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/footer.py +0 -0
  296. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/header.py +0 -0
  297. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/navigation.py +0 -0
  298. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/prompt.py +0 -0
  299. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/hubtui/refresh.py +0 -0
  300. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/paths.py +0 -0
  301. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/quota.py +0 -0
  302. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/selection.py +0 -0
  303. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/sidebar_rows.py +0 -0
  304. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/widgets/chip.py +0 -0
  305. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/widgets/divider.py +0 -0
  306. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/widgets/header_bar.py +0 -0
  307. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/widgets/preview_bar.py +0 -0
  308. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/tui/widgets/sidebar_row.py +0 -0
  309. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/web/__init__.py +0 -0
  310. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/web/__main__.py +0 -0
  311. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/mixhub/web/server.py +0 -0
  312. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/browser_check.mjs +0 -0
  313. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/README.md +0 -0
  314. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/mixhub_cli.py +0 -0
  315. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/compat/proc_guard.py +0 -0
  316. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/conftest.py +0 -0
  317. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/fixtures/agy_1_4_7_altscreen_typed_120x40.bin +0 -0
  318. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/fixtures/agy_1_4_7_altscreen_wrap_paste_40x30.bin +0 -0
  319. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/mcp_client.mjs +0 -0
  320. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_agy_filter_mouse.py +0 -0
  321. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_bg_proc_scan.py +0 -0
  322. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_caffeinate.py +0 -0
  323. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_env_names.py +0 -0
  324. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_opencode.py +0 -0
  325. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_packaging.py +0 -0
  326. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_proc_guard.py +0 -0
  327. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_providers_live.py +0 -0
  328. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_rules_sync.py +0 -0
  329. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_schema_resilience.py +0 -0
  330. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_statusline_command.py +0 -0
  331. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_subagent_filter.py +0 -0
  332. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_test_isolation.py +0 -0
  333. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_tui_module_layout.py +0 -0
  334. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_update.py +0 -0
  335. {mixhub_cli-0.2.0 → mixhub_cli-0.2.1}/tests/test_web_navigation.py +0 -0
@@ -0,0 +1,28 @@
1
+ name: publish
2
+
3
+ # Push a tag like v0.2.1 to build and upload that version to PyPI.
4
+ # PyPI trusts this workflow (Trusted Publishing), so no token is stored anywhere.
5
+ on:
6
+ push:
7
+ tags: ["v*"]
8
+
9
+ jobs:
10
+ pypi:
11
+ runs-on: ubuntu-latest
12
+ environment: pypi
13
+ permissions:
14
+ id-token: write
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-python@v5
18
+ with:
19
+ python-version: "3.13"
20
+ - name: Tag matches the package version
21
+ run: |
22
+ v=$(python -c "import mixhub; print(mixhub.__version__)")
23
+ test "v$v" = "$GITHUB_REF_NAME" || { echo "tag $GITHUB_REF_NAME, package $v"; exit 1; }
24
+ - name: Build
25
+ run: |
26
+ python -m pip install -q build
27
+ python -m build
28
+ - uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,34 @@
1
+ name: test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ workflow_dispatch:
8
+
9
+ jobs:
10
+ test:
11
+ strategy:
12
+ fail-fast: false
13
+ matrix:
14
+ include:
15
+ - os: ubuntu-latest
16
+ python: "3.10"
17
+ - os: ubuntu-latest
18
+ python: "3.13"
19
+ - os: macos-latest
20
+ python: "3.13"
21
+ runs-on: ${{ matrix.os }}
22
+ timeout-minutes: 15
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - uses: actions/setup-python@v5
26
+ with:
27
+ python-version: ${{ matrix.python }}
28
+ - name: tmux
29
+ run: |
30
+ if [ "$RUNNER_OS" = Linux ]; then sudo apt-get install -y -qq tmux; else brew install tmux; fi
31
+ tmux -V
32
+ - run: python -m pip install -q pytest
33
+ - run: python -m pytest -q -o faulthandler_timeout=60 tests/ # a hung test prints where it is stuck
34
+ - run: node --test tests/webui_composer.test.mjs
@@ -58,3 +58,6 @@ tests/test_git_steward.py
58
58
  # local launchers for processes started before the root scripts moved
59
59
  /tui.py
60
60
  /slack_bridge.py
61
+
62
+ # Maintainer-only two-way extras (Slack bridge, Telegram approvals); imports mixhub, never the reverse
63
+ /mixhub_private/
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: mixhub-cli
3
- Version: 0.2.0
4
- Summary: One switchboard for Claude Code, Gemini (AGY), Codex and OpenCode sessions: a tmux workspace, a web workbench and handoff between agents.
3
+ Version: 0.2.1
4
+ Summary: A terminal sidebar for your Claude Code, Codex, Gemini and OpenCode sessions.
5
5
  Project-URL: Homepage, https://github.com/ShinoKana/mixhub
6
6
  Project-URL: Repository, https://github.com/ShinoKana/mixhub.git
7
7
  Project-URL: Issues, https://github.com/ShinoKana/mixhub/issues
@@ -29,43 +29,52 @@ Description-Content-Type: text/markdown
29
29
 
30
30
  # MixHub
31
31
 
32
- **One switchboard for Claude Code, Gemini (AGY), Codex and OpenCode sessions**
32
+ **A terminal sidebar for your Claude Code, Codex, Gemini and OpenCode sessions**
33
33
 
34
34
  [![License: MIT](https://img.shields.io/badge/license-MIT-596F62.svg)](#license)
35
35
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB.svg)](#install)
36
36
  [![Dependencies: stdlib](https://img.shields.io/badge/python_dependencies-stdlib_only-596F62.svg)](#architecture)
37
37
 
38
- A `tmux` workspace that lists every coding-agent session on your machine in one sidebar, previews any transcript, and runs the real CLI in the right pane. MixHub reads each agent's own files and never patches the agents.
39
-
40
38
  </div>
41
39
 
42
- ```text
43
- ┌─ SIDEBAR (32 cols) ────────────┬─ RIGHT PANE (PREVIEW OR LIVE CLI) ───────────────────────────┐
44
- │ ⌄ mixhub ‹ › ◧ │ ~/workspace/project · a1b2c3d4 │
45
- │ │ ◇ Auth flow │
46
- │ ✎ New session │ ────────────────────────────────────────────────────────── │
47
- │ ▾ Pinned 1 │ ─ │
48
- │ ◇ Auth flow ★ │ > Refactor the state machine and run the tests ━ │
49
- │ ▾ Active 2 │ Edited rules_sync.py (+42 -8) ─ │
50
- │ ◈ Payment webhook 2m │ Ran 43 tests in 0.14s · OK ─ │
51
- │ ◇ API rate limit 5m │ ─ │
52
- │ ▾ Recent 4 │ > Check idempotency on retry ━ │
53
- │ · Database migration 3h │ Verified retry keys and added unit coverage. ─ │
54
- │ · OAuth token refresh 1d │ │
55
- │ claude 83% 03:00 60% 10/30 │ ────────────────────────────────────────────────────────── │
56
- │ codex 69% 04:25 41% 10/31 │ [ Resume ↵ ] [ Go to Tab ] │
57
- │ gemini 86% 02:29 21% 11/01 │ │
58
- │ ▴ cpu 12% mem 64% ↗ web │ │
59
- └────────────────────────────────┴──────────────────────────────────────────────────────────────┘
40
+ MixHub is a tmux workspace that lists every coding-agent session on your machine, including the ones you started in other terminal tabs, and shows whether each one is working, waiting for you or done. Click a session and its real CLI opens on the right. The others keep running in the background.
41
+
42
+ ```bash
43
+ pipx install mixhub-cli # or: uv tool install mixhub-cli
44
+ mixhub
60
45
  ```
61
46
 
47
+ Early, used daily on macOS with around a dozen sessions. Linux works but gets less testing. Needs Python 3.10+ and tmux.
48
+
49
+ https://github.com/user-attachments/assets/3c068cd5-b168-4b5c-96a0-208c8a6a908d
50
+
62
51
  ## Why
63
52
 
64
- Agent CLIs keep getting better at what happens inside one session. What they don't do is look across sessions: which of my twelve sessions is waiting for me, which one finished while I was away, which agent still has quota left, and how do I hand a half-done task to another agent. MixHub covers that part and nothing else:
53
+ I usually have several agents going at once, often from different vendors. I kept missing the one that had been sitting on a permission prompt for ten minutes, and losing sessions I had started in some other tab. A terminal tab shows a title, not whether the agent behind it is stuck. MixHub works that out from each agent's own session files and puts all of them in one list.
54
+
55
+ What it does:
56
+
57
+ - Lists sessions from Claude Code, Codex, Gemini (Antigravity CLI) and OpenCode, wherever they were started.
58
+ - Marks each one as working, waiting for approval, done or unread. Status comes from the agents' session files; reading the screen is only a fallback.
59
+ - Runs the unmodified CLI in the right pane. Switching sessions switches a tmux window, so nothing is interrupted.
60
+ - Shows 5-hour and weekly quota for each agent at the bottom of the sidebar.
61
+ - Rings the terminal when a session finishes or starts waiting while you're elsewhere.
65
62
 
66
- - It reads the native transcript files and databases in place. There is no import step and no second copy of your history; `claude --resume <id>` keeps working as before.
67
- - The right pane runs the unmodified CLI. When an agent ships an update, you get it in MixHub without waiting for MixHub.
68
- - It only adds things that stay useful as agents improve: a cross-agent index, a process pool, quota at a glance, handoff between agents, and notifications.
63
+ What it doesn't do: it doesn't create worktrees or touch git, doesn't patch the agents, and doesn't send anything off your machine unless you set up a notification channel (otherwise the only network call is a daily update check, which you can turn off). It uses its own private tmux servers, so your own tmux config and sessions are left alone.
64
+
65
+ ## Similar tools
66
+
67
+ | | Agents | Runs in | Git worktrees | Per-session status | Sessions started elsewhere |
68
+ | :--- | :--- | :--- | :--- | :--- | :--- |
69
+ | MixHub | Claude Code, Codex, Gemini, OpenCode | any terminal (needs tmux) | no | from session files | listed |
70
+ | [Claude Code Agent View](https://code.claude.com/docs/en/agent-view) | Claude Code | the `claude` CLI | background sessions by default | yes | only after `/bg` or `claude --bg` |
71
+ | [cmux](https://github.com/manaflow-ai/cmux) | Claude Code, Codex, OpenCode, Gemini, Amp and more | its own macOS app | no | notifications via hooks / OSC | not documented |
72
+ | [claude-squad](https://github.com/smtg-ai/claude-squad) | Claude Code, Codex, Gemini, Aider | any terminal (needs tmux) | one per session | not documented | no, manages its own |
73
+ | [ccmanager](https://github.com/kbwo/ccmanager) | Claude Code, Codex, Gemini, OpenCode and more | any terminal | yes, managed in the app | busy / waiting / idle | not documented |
74
+ | [agent-deck](https://github.com/asheshgoplani/agent-deck) | Claude Code, Codex, Gemini, OpenCode and more | any terminal (tmux) | optional | running / waiting / idle | its own tmux sessions |
75
+ | [tmux-agent-status](https://github.com/samleeney/tmux-agent-status) | Claude Code, Codex, Devin | inside your tmux | no | via agent hooks | inside tmux |
76
+
77
+ If you only use Claude Code, Agent View is built in. If you want a worktree per task, claude-squad and ccmanager do that and MixHub doesn't.
69
78
 
70
79
  ## Install
71
80
 
@@ -86,7 +95,7 @@ The script installs the `mixhub-cli` package (it provides the `mixhub` command)
86
95
  ```bash
87
96
  mixhub # open or reattach to the workspace
88
97
  mixhub web # local web workbench at http://127.0.0.1:8765
89
- mixhub doctor # check providers, channels, locks and terminal settings
98
+ mixhub doctor # check agent CLIs, tmux, notification channels and terminal settings
90
99
  ```
91
100
 
92
101
  From a clone:
@@ -118,13 +127,13 @@ mixhub update --check # only say whether a newer version exists
118
127
 
119
128
  ## The workspace
120
129
 
121
- **Sidebar.** Sessions are grouped into `Pinned`, `Active` (sessions you are working with) and `Recent`. A row shows the agent's brand-colored badge, the title the agent itself gave the session, and its age. The badge animates while the agent is working and turns into a waiting mark when it needs approval or an answer. Unread replies are bold. `▾ mixhub` filters by agent, `‹ ›` walks back and forward through what you looked at, `◧` collapses the sidebar. Hover a row to pin it. The sidebar is entirely click-driven; keys go to the agent.
130
+ **Sidebar.** Sessions are grouped into `Pinned`, `Active` (sessions you are working with) and `Recent`. A row shows the agent's brand-colored badge, the title the agent itself gave the session, and its age. The badge animates while the agent is working; when the agent needs approval or an answer it stops, turns bold amber, and the age turns yellow. Unread replies are bold. `▾ mixhub` filters by agent, `‹ ›` walks back and forward through what you looked at, `◧` collapses the sidebar. Hover a row to pin it. The sidebar is entirely click-driven; keys go to the agent. Subagent runs, headless or scripted runs, and sessions whose working directory is under `/tmp` are not listed.
122
131
 
123
132
  **Right pane.** Clicking a session that is running in MixHub switches to its live CLI window. Clicking any other session shows a read-only transcript preview with a turn minimap; `[ Resume ↵ ]` starts the CLI in place, and `[ Go to Tab ]` jumps to a session that is running in another Terminal tab. Each live window has a two-row header with the working directory, session id and title.
124
133
 
125
134
  **Home.** With nothing selected, the right pane holds a plain login shell under the preview, for quick commands without leaving the workspace. Clear it the usual way (`clear` or Ctrl+L).
126
135
 
127
- **Footer.** Remaining `5h` and weekly quota with reset times for each agent that has at least one session on this machine, plus CPU and memory. Codex quota comes from its own session files. For exact Claude Code and Gemini (AGY) numbers, point their status line at MixHub, which records the quota each agent reports and prints a short status line back:
136
+ **Footer.** Used `5h` and weekly quota with reset times for each agent that has at least one session on this machine, plus CPU and memory. Codex quota comes from its own session files. For exact Claude Code and Gemini (AGY) numbers, point their status line at MixHub, which records the quota each agent reports and prints a short status line back:
128
137
 
129
138
  ```jsonc
130
139
  // ~/.claude/settings.json
@@ -144,15 +153,11 @@ Without it the footer estimates Claude's quota from transcripts.
144
153
 
145
154
  ## Session lifecycle
146
155
 
147
- MixHub separates whether a session is listed as `Active` from whether its process is running. Idle live sessions are put to sleep (process stopped, session kept in `Active`) and resumed in place when you click them:
156
+ MixHub separates whether a session is listed as `Active` from whether its process is running. Sessions keep running until you `/exit` them or close them from the sidebar. To have idle ones put to sleep (process stopped, session kept in `Active`, resumed in place when you click it), set `auto_sleep_hours` in `Preferences`; it is off by default.
148
157
 
149
- | Condition | Idle time before sleep |
150
- | :--- | :--- |
151
- | Normal | 12 hours |
152
- | Moderate memory pressure | 6 hours |
153
- | Critical memory pressure, or more than 6 live sessions | 4 hours |
158
+ A session is never put to sleep while it is working or waiting for approval, has running subagents or open background tasks, or has user processes running on its terminal. If the process table can't be read, nothing is put to sleep. Only sessions running in MixHub's own windows are ever put to sleep, automatically or from the web; an agent you started in another terminal is listed but never stopped. `/exit` in the agent moves the session to `Recent`; a system shutdown keeps `Active` as it was.
154
159
 
155
- A session is never put to sleep while it is working or waiting for approval, has running subagents or open background tasks, or has user processes running on its terminal. If the process table can't be read, nothing is put to sleep. `/exit` in the agent moves the session to `Recent`; a system shutdown keeps `Active` as it was.
160
+ On macOS, while an agent is working and the Mac runs on power, MixHub keeps it from idle-sleeping (`caffeinate -i`, released when the work ends; never on battery).
156
161
 
157
162
  ## Handoff between agents
158
163
 
@@ -164,7 +169,7 @@ mixhub handoff SESSION_ID --to claude --mode review
164
169
  mixhub handoff SESSION_ID --to agy --dry-run
165
170
  ```
166
171
 
167
- The new session is titled `↳ <source title>`. A write lease per working tree records which session is allowed to write, and a relay to a busy session is held until its turn ends.
172
+ The new session is titled `↳ <source title>`.
168
173
 
169
174
  ## Web workbench and notifications
170
175
 
@@ -173,8 +178,45 @@ The new session is titled `↳ <source title>`. A write lease per working tree r
173
178
  <img alt="The MixHub web workbench: a session list with quota on the left and a Claude Code transcript on the right" src=".github/assets/web-light.png">
174
179
  </picture>
175
180
 
176
- - `mixhub web` serves a loopback-only web UI: search across all sessions, full transcripts, and a phone-friendly bar for approving or stopping a waiting session.
177
- - `mixhub notify` and the attention hook can send to Telegram, Discord, Slack (Socket Mode bridge), Feishu/Lark, or any webhook or command, with `always`, `auto_idle` and `manual` modes.
181
+ - `mixhub web` serves a loopback-only web UI: search across all sessions and full transcripts. The terminal workspace is where MixHub gets used day to day; the web workbench sees much less use and has rougher edges, so please [open an issue](../../issues) when something there looks wrong.
182
+ - `mixhub notify` sends a one-way message to Telegram, a Slack incoming webhook or any URL that takes a JSON POST. It only tells you something happened; replying and approving stay in the agents' own remote control.
183
+
184
+ ### Notification channels
185
+
186
+ Nothing is sent until you set up a channel in `~/.config/mixhub/channels.json`. Every section is optional:
187
+
188
+ ```json
189
+ {
190
+ "telegram": {
191
+ "bot_token": "123456:ABC...",
192
+ "chat_id": -1001234567890,
193
+ "topics": {"builds": 42, "reviews": 57},
194
+ "default_topic": "builds",
195
+ "routing_rules": {"reviews": ["review", "pr"]}
196
+ },
197
+ "slack": {"webhook_url": "https://hooks.slack.com/services/..."},
198
+ "webhook": {"url": "https://example.com/mixhub", "headers": {"Authorization": "Bearer ..."}}
199
+ }
200
+ ```
201
+
202
+ - Telegram goes through the Bot API (`sendMessage`, plus `sendDocument` for an attached file). `topics` maps forum topic names to thread ids. A message goes to the topic you name with `--topic`, else the first topic whose `routing_rules` keyword appears in the session's folder, title or the message, else `default_topic`, else the chat itself. To send through your own command instead, set `"tgnotify_bin"`. It is called as `<bin> -t <topic> [--alert] [--file F] <text>`.
203
+ - Slack posts to the channel the incoming webhook belongs to.
204
+ - `webhook` receives `{"text", "title", "session_id", "tool", "topic"}`.
205
+
206
+ The notification settings in the web workbench (the bell) show which channels are set up and send a test message.
207
+
208
+ ```bash
209
+ mixhub notify "Deploy finished" # every configured channel
210
+ mixhub notify --channel telegram --topic builds --alert "Tests failed"
211
+ mixhub notify --session 1a2b3c # a short card about that session
212
+ ```
213
+
214
+ To hear when an agent needs you or finishes a turn, add the agents' own hooks. `mixhub notify --print-hooks` prints both snippets with the full path to `mixhub`:
215
+
216
+ - Claude Code (`~/.claude/settings.json`): `Notification` and `Stop` hooks running `mixhub notify --hook claude`. You get the prompt Claude is waiting on, or the start of its last reply.
217
+ - Codex (`~/.codex/config.toml`): `notify = ["mixhub", "notify", "--hook", "codex"]`, which sends the last reply after each turn.
218
+
219
+ A hook sends to every configured channel, sends nothing when none is configured, and always exits 0.
178
220
 
179
221
  ## Language
180
222
 
@@ -189,9 +231,9 @@ The web workbench and its API listen on `127.0.0.1` only and have no login. They
189
231
  - Other programs and other users on the same machine can reach it. Don't run MixHub on a shared machine you don't trust.
190
232
  - Requests from websites in your browser are refused: the server checks the `Host` header (against DNS rebinding), `Origin` and `Sec-Fetch-Site`, only accepts JSON for anything that changes state, and sends a strict Content-Security-Policy.
191
233
  - It only answers to `127.0.0.1` and `localhost`, so a reverse proxy or a forwarded port won't work, and shouldn't. To open it from another device, use an SSH tunnel (`ssh -L 8765:127.0.0.1:8765 your-machine`, then `http://localhost:8765`).
192
- - Notification channels (Telegram, Slack, Discord, Feishu, webhooks) send session titles and message text to those services. Nothing is sent unless you configure a channel.
234
+ - Notification channels (Telegram, Slack, webhooks) send session titles and message text to those services. Nothing is sent unless you configure a channel.
193
235
 
194
- MixHub reads the agents' transcripts and databases without changing them. It writes to agent files only when you ask it to: renaming a session (the title goes where that agent keeps titles), workspace trust you opted into with `trust_all_workspaces` or `trust_roots` (so a resume doesn't stop at the trust prompt), `mixhub slim --apply` (replaces old screenshots in Claude transcripts with a placeholder) and `mixhub sync-rules` (writes `.gemini/GEMINI.md` from `AGENTS.md`).
236
+ MixHub reads the agents' transcripts and databases without changing them. It writes to agent files only when you ask it to: renaming a session (the title goes where that agent keeps titles, also while the session runs: a title record appended to a Claude transcript, a row updated in the Codex or Gemini database), workspace trust you opted into with `trust_all_workspaces` or `trust_roots` (so a resume doesn't stop at the trust prompt) and `mixhub sync-rules` (writes `.gemini/GEMINI.md` from `AGENTS.md`).
195
237
 
196
238
  ## Configuration
197
239
 
@@ -202,13 +244,13 @@ Everything works without configuration. Optional settings live in a JSON file na
202
244
  ```json
203
245
  {
204
246
  "language": "auto",
205
- "user_name": "Developer",
206
- "claude_theme": false,
247
+ "host_label": "work-laptop",
207
248
  "trust_all_workspaces": false,
208
249
  "trust_roots": ["~/workspace"],
209
250
  "agy_mouse_shim": "auto",
210
251
  "dispatch_scripts": ["my-agent-runner"],
211
- "update_check": true
252
+ "update_check": true,
253
+ "auto_sleep_hours": 0
212
254
  }
213
255
  ```
214
256
 
@@ -223,7 +265,7 @@ Everything works without configuration. Optional settings live in a JSON file na
223
265
  | `MIXHUB_NO_OPEN=1` | `mixhub web` starts the daemon without opening a browser |
224
266
  | `MIXHUB_NO_UPDATE_CHECK=1` | No daily update check |
225
267
 
226
- `dispatch_scripts` names your own wrapper scripts that start agents headlessly, so their sessions are not mistaken for interactive ones. Prompts that your own automation sends can be hidden from the list with `data/local_rules.json` (`automated_prefixes`, `automated_substrings`).
268
+ `host_label` is the machine name shown in the web workbench (the hostname by default). `dispatch_scripts` names your own wrapper scripts that start agents headlessly, so their sessions are not mistaken for interactive ones. Prompts that your own automation sends can be hidden from the list with `data/local_rules.json` (`automated_prefixes`, `automated_substrings`). Notes your own tools type into an agent on another agent's behalf go in `relayed_prefixes`: they never count as you having used a session, but don't hide one either.
227
269
 
228
270
  ## Architecture
229
271
 
@@ -235,24 +277,23 @@ Python standard library only. The code lives in the `mixhub` package:
235
277
  | `mixhub/providers` | Per-agent launch and resume commands, quota reading, native title sync, harness presets |
236
278
  | `mixhub/tui` | The curses sidebar, preview reader and window header (`HubTUI`, composed from one mixin per concern) |
237
279
  | `mixhub/tmux` | The two tmux servers: outer workspace, the `hubpool` window pool, headers, home shell |
238
- | `mixhub/handoff` | Handoff extraction and rendering, write leases, hold queue, clean forks |
239
- | `mixhub/channels` | Notification adapters, approval ledger, away receipts, Slack bridge |
280
+ | `mixhub/handoff` | Handoff extraction and rendering |
281
+ | `mixhub/channels` | One-way notifications: Telegram Bot API, Slack incoming webhook |
240
282
  | `mixhub/probe` | Reading an agent's screen: working, waiting for approval, menus |
241
283
  | `mixhub/proc` | Process identity and classification, safe-kill guards |
242
284
  | `mixhub/rules` | Shared agent rules compiler and workspace trust |
243
285
  | `mixhub/filters/agy_claude` | Optional PTY filter for AGY (theme, cursor, mouse) |
244
286
  | `mixhub/web` | Loopback HTTP API and assets for the web workbench |
245
287
  | `mixhub/cli` | The `mixhub` command |
246
- | `mixhub/core` | Shared paths, launch commands, preferences, plugin protocols |
288
+ | `mixhub/core` | Shared paths, launch commands, preferences, session states |
247
289
 
248
290
  State (pins, working set, caches, logs) lives in `~/.local/share/mixhub` for an installed package, or next to the code in a git checkout; set `MIXHUB_HOME` to put it elsewhere.
249
291
 
250
- The workspace is two tmux servers. The outer session holds the sidebar on the left and, on the right, a client of a private `tmux -L hubpool` server where every live agent runs in its own window. Switching sessions switches that window, so agents keep running untouched while you look elsewhere.
292
+ The workspace is two private tmux servers, neither of them your default one, so MixHub's sessions, options, key bindings and hooks never reach your own tmux. The outer session (`tmux -L mixhub`) holds the sidebar on the left and, on the right, a client of `tmux -L hubpool`, where every live agent runs in its own window. Switching sessions switches that window, so agents keep running untouched while you look elsewhere. Run `mixhub` inside your own tmux and the workspace opens nested in that pane.
251
293
 
252
294
  ## Roadmap
253
295
 
254
296
  - An agent-facing command set (list sessions, read another session's recent turns) and a skill so agents can use MixHub themselves.
255
- - Two-way chat channels: replies in a Telegram topic delivered into the matching session.
256
297
  - Broader Linux polish.
257
298
 
258
299
  ## Development
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "language": "auto",
3
- "user_name": "Developer",
4
- "claude_theme": false,
3
+ "host_label": "work-laptop",
5
4
  "trust_all_workspaces": false,
6
5
  "trust_roots": ["~/workspace"],
7
6
  "agy_mouse_shim": "auto",
8
7
  "dispatch_scripts": ["my-agent-runner"],
9
- "update_check": true
8
+ "update_check": true,
9
+ "auto_sleep_hours": 0
10
10
  }
@@ -2,43 +2,52 @@
2
2
 
3
3
  # MixHub
4
4
 
5
- **One switchboard for Claude Code, Gemini (AGY), Codex and OpenCode sessions**
5
+ **A terminal sidebar for your Claude Code, Codex, Gemini and OpenCode sessions**
6
6
 
7
7
  [![License: MIT](https://img.shields.io/badge/license-MIT-596F62.svg)](#license)
8
8
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB.svg)](#install)
9
9
  [![Dependencies: stdlib](https://img.shields.io/badge/python_dependencies-stdlib_only-596F62.svg)](#architecture)
10
10
 
11
- A `tmux` workspace that lists every coding-agent session on your machine in one sidebar, previews any transcript, and runs the real CLI in the right pane. MixHub reads each agent's own files and never patches the agents.
12
-
13
11
  </div>
14
12
 
15
- ```text
16
- ┌─ SIDEBAR (32 cols) ────────────┬─ RIGHT PANE (PREVIEW OR LIVE CLI) ───────────────────────────┐
17
- │ ⌄ mixhub ‹ › ◧ │ ~/workspace/project · a1b2c3d4 │
18
- │ │ ◇ Auth flow │
19
- │ ✎ New session │ ────────────────────────────────────────────────────────── │
20
- │ ▾ Pinned 1 │ ─ │
21
- │ ◇ Auth flow ★ │ > Refactor the state machine and run the tests ━ │
22
- │ ▾ Active 2 │ Edited rules_sync.py (+42 -8) ─ │
23
- │ ◈ Payment webhook 2m │ Ran 43 tests in 0.14s · OK ─ │
24
- │ ◇ API rate limit 5m │ ─ │
25
- │ ▾ Recent 4 │ > Check idempotency on retry ━ │
26
- │ · Database migration 3h │ Verified retry keys and added unit coverage. ─ │
27
- │ · OAuth token refresh 1d │ │
28
- │ claude 83% 03:00 60% 10/30 │ ────────────────────────────────────────────────────────── │
29
- │ codex 69% 04:25 41% 10/31 │ [ Resume ↵ ] [ Go to Tab ] │
30
- │ gemini 86% 02:29 21% 11/01 │ │
31
- │ ▴ cpu 12% mem 64% ↗ web │ │
32
- └────────────────────────────────┴──────────────────────────────────────────────────────────────┘
13
+ MixHub is a tmux workspace that lists every coding-agent session on your machine, including the ones you started in other terminal tabs, and shows whether each one is working, waiting for you or done. Click a session and its real CLI opens on the right. The others keep running in the background.
14
+
15
+ ```bash
16
+ pipx install mixhub-cli # or: uv tool install mixhub-cli
17
+ mixhub
33
18
  ```
34
19
 
20
+ Early, used daily on macOS with around a dozen sessions. Linux works but gets less testing. Needs Python 3.10+ and tmux.
21
+
22
+ https://github.com/user-attachments/assets/3c068cd5-b168-4b5c-96a0-208c8a6a908d
23
+
35
24
  ## Why
36
25
 
37
- Agent CLIs keep getting better at what happens inside one session. What they don't do is look across sessions: which of my twelve sessions is waiting for me, which one finished while I was away, which agent still has quota left, and how do I hand a half-done task to another agent. MixHub covers that part and nothing else:
26
+ I usually have several agents going at once, often from different vendors. I kept missing the one that had been sitting on a permission prompt for ten minutes, and losing sessions I had started in some other tab. A terminal tab shows a title, not whether the agent behind it is stuck. MixHub works that out from each agent's own session files and puts all of them in one list.
27
+
28
+ What it does:
29
+
30
+ - Lists sessions from Claude Code, Codex, Gemini (Antigravity CLI) and OpenCode, wherever they were started.
31
+ - Marks each one as working, waiting for approval, done or unread. Status comes from the agents' session files; reading the screen is only a fallback.
32
+ - Runs the unmodified CLI in the right pane. Switching sessions switches a tmux window, so nothing is interrupted.
33
+ - Shows 5-hour and weekly quota for each agent at the bottom of the sidebar.
34
+ - Rings the terminal when a session finishes or starts waiting while you're elsewhere.
38
35
 
39
- - It reads the native transcript files and databases in place. There is no import step and no second copy of your history; `claude --resume <id>` keeps working as before.
40
- - The right pane runs the unmodified CLI. When an agent ships an update, you get it in MixHub without waiting for MixHub.
41
- - It only adds things that stay useful as agents improve: a cross-agent index, a process pool, quota at a glance, handoff between agents, and notifications.
36
+ What it doesn't do: it doesn't create worktrees or touch git, doesn't patch the agents, and doesn't send anything off your machine unless you set up a notification channel (otherwise the only network call is a daily update check, which you can turn off). It uses its own private tmux servers, so your own tmux config and sessions are left alone.
37
+
38
+ ## Similar tools
39
+
40
+ | | Agents | Runs in | Git worktrees | Per-session status | Sessions started elsewhere |
41
+ | :--- | :--- | :--- | :--- | :--- | :--- |
42
+ | MixHub | Claude Code, Codex, Gemini, OpenCode | any terminal (needs tmux) | no | from session files | listed |
43
+ | [Claude Code Agent View](https://code.claude.com/docs/en/agent-view) | Claude Code | the `claude` CLI | background sessions by default | yes | only after `/bg` or `claude --bg` |
44
+ | [cmux](https://github.com/manaflow-ai/cmux) | Claude Code, Codex, OpenCode, Gemini, Amp and more | its own macOS app | no | notifications via hooks / OSC | not documented |
45
+ | [claude-squad](https://github.com/smtg-ai/claude-squad) | Claude Code, Codex, Gemini, Aider | any terminal (needs tmux) | one per session | not documented | no, manages its own |
46
+ | [ccmanager](https://github.com/kbwo/ccmanager) | Claude Code, Codex, Gemini, OpenCode and more | any terminal | yes, managed in the app | busy / waiting / idle | not documented |
47
+ | [agent-deck](https://github.com/asheshgoplani/agent-deck) | Claude Code, Codex, Gemini, OpenCode and more | any terminal (tmux) | optional | running / waiting / idle | its own tmux sessions |
48
+ | [tmux-agent-status](https://github.com/samleeney/tmux-agent-status) | Claude Code, Codex, Devin | inside your tmux | no | via agent hooks | inside tmux |
49
+
50
+ If you only use Claude Code, Agent View is built in. If you want a worktree per task, claude-squad and ccmanager do that and MixHub doesn't.
42
51
 
43
52
  ## Install
44
53
 
@@ -59,7 +68,7 @@ The script installs the `mixhub-cli` package (it provides the `mixhub` command)
59
68
  ```bash
60
69
  mixhub # open or reattach to the workspace
61
70
  mixhub web # local web workbench at http://127.0.0.1:8765
62
- mixhub doctor # check providers, channels, locks and terminal settings
71
+ mixhub doctor # check agent CLIs, tmux, notification channels and terminal settings
63
72
  ```
64
73
 
65
74
  From a clone:
@@ -91,13 +100,13 @@ mixhub update --check # only say whether a newer version exists
91
100
 
92
101
  ## The workspace
93
102
 
94
- **Sidebar.** Sessions are grouped into `Pinned`, `Active` (sessions you are working with) and `Recent`. A row shows the agent's brand-colored badge, the title the agent itself gave the session, and its age. The badge animates while the agent is working and turns into a waiting mark when it needs approval or an answer. Unread replies are bold. `▾ mixhub` filters by agent, `‹ ›` walks back and forward through what you looked at, `◧` collapses the sidebar. Hover a row to pin it. The sidebar is entirely click-driven; keys go to the agent.
103
+ **Sidebar.** Sessions are grouped into `Pinned`, `Active` (sessions you are working with) and `Recent`. A row shows the agent's brand-colored badge, the title the agent itself gave the session, and its age. The badge animates while the agent is working; when the agent needs approval or an answer it stops, turns bold amber, and the age turns yellow. Unread replies are bold. `▾ mixhub` filters by agent, `‹ ›` walks back and forward through what you looked at, `◧` collapses the sidebar. Hover a row to pin it. The sidebar is entirely click-driven; keys go to the agent. Subagent runs, headless or scripted runs, and sessions whose working directory is under `/tmp` are not listed.
95
104
 
96
105
  **Right pane.** Clicking a session that is running in MixHub switches to its live CLI window. Clicking any other session shows a read-only transcript preview with a turn minimap; `[ Resume ↵ ]` starts the CLI in place, and `[ Go to Tab ]` jumps to a session that is running in another Terminal tab. Each live window has a two-row header with the working directory, session id and title.
97
106
 
98
107
  **Home.** With nothing selected, the right pane holds a plain login shell under the preview, for quick commands without leaving the workspace. Clear it the usual way (`clear` or Ctrl+L).
99
108
 
100
- **Footer.** Remaining `5h` and weekly quota with reset times for each agent that has at least one session on this machine, plus CPU and memory. Codex quota comes from its own session files. For exact Claude Code and Gemini (AGY) numbers, point their status line at MixHub, which records the quota each agent reports and prints a short status line back:
109
+ **Footer.** Used `5h` and weekly quota with reset times for each agent that has at least one session on this machine, plus CPU and memory. Codex quota comes from its own session files. For exact Claude Code and Gemini (AGY) numbers, point their status line at MixHub, which records the quota each agent reports and prints a short status line back:
101
110
 
102
111
  ```jsonc
103
112
  // ~/.claude/settings.json
@@ -117,15 +126,11 @@ Without it the footer estimates Claude's quota from transcripts.
117
126
 
118
127
  ## Session lifecycle
119
128
 
120
- MixHub separates whether a session is listed as `Active` from whether its process is running. Idle live sessions are put to sleep (process stopped, session kept in `Active`) and resumed in place when you click them:
129
+ MixHub separates whether a session is listed as `Active` from whether its process is running. Sessions keep running until you `/exit` them or close them from the sidebar. To have idle ones put to sleep (process stopped, session kept in `Active`, resumed in place when you click it), set `auto_sleep_hours` in `Preferences`; it is off by default.
121
130
 
122
- | Condition | Idle time before sleep |
123
- | :--- | :--- |
124
- | Normal | 12 hours |
125
- | Moderate memory pressure | 6 hours |
126
- | Critical memory pressure, or more than 6 live sessions | 4 hours |
131
+ A session is never put to sleep while it is working or waiting for approval, has running subagents or open background tasks, or has user processes running on its terminal. If the process table can't be read, nothing is put to sleep. Only sessions running in MixHub's own windows are ever put to sleep, automatically or from the web; an agent you started in another terminal is listed but never stopped. `/exit` in the agent moves the session to `Recent`; a system shutdown keeps `Active` as it was.
127
132
 
128
- A session is never put to sleep while it is working or waiting for approval, has running subagents or open background tasks, or has user processes running on its terminal. If the process table can't be read, nothing is put to sleep. `/exit` in the agent moves the session to `Recent`; a system shutdown keeps `Active` as it was.
133
+ On macOS, while an agent is working and the Mac runs on power, MixHub keeps it from idle-sleeping (`caffeinate -i`, released when the work ends; never on battery).
129
134
 
130
135
  ## Handoff between agents
131
136
 
@@ -137,7 +142,7 @@ mixhub handoff SESSION_ID --to claude --mode review
137
142
  mixhub handoff SESSION_ID --to agy --dry-run
138
143
  ```
139
144
 
140
- The new session is titled `↳ <source title>`. A write lease per working tree records which session is allowed to write, and a relay to a busy session is held until its turn ends.
145
+ The new session is titled `↳ <source title>`.
141
146
 
142
147
  ## Web workbench and notifications
143
148
 
@@ -146,8 +151,45 @@ The new session is titled `↳ <source title>`. A write lease per working tree r
146
151
  <img alt="The MixHub web workbench: a session list with quota on the left and a Claude Code transcript on the right" src=".github/assets/web-light.png">
147
152
  </picture>
148
153
 
149
- - `mixhub web` serves a loopback-only web UI: search across all sessions, full transcripts, and a phone-friendly bar for approving or stopping a waiting session.
150
- - `mixhub notify` and the attention hook can send to Telegram, Discord, Slack (Socket Mode bridge), Feishu/Lark, or any webhook or command, with `always`, `auto_idle` and `manual` modes.
154
+ - `mixhub web` serves a loopback-only web UI: search across all sessions and full transcripts. The terminal workspace is where MixHub gets used day to day; the web workbench sees much less use and has rougher edges, so please [open an issue](../../issues) when something there looks wrong.
155
+ - `mixhub notify` sends a one-way message to Telegram, a Slack incoming webhook or any URL that takes a JSON POST. It only tells you something happened; replying and approving stay in the agents' own remote control.
156
+
157
+ ### Notification channels
158
+
159
+ Nothing is sent until you set up a channel in `~/.config/mixhub/channels.json`. Every section is optional:
160
+
161
+ ```json
162
+ {
163
+ "telegram": {
164
+ "bot_token": "123456:ABC...",
165
+ "chat_id": -1001234567890,
166
+ "topics": {"builds": 42, "reviews": 57},
167
+ "default_topic": "builds",
168
+ "routing_rules": {"reviews": ["review", "pr"]}
169
+ },
170
+ "slack": {"webhook_url": "https://hooks.slack.com/services/..."},
171
+ "webhook": {"url": "https://example.com/mixhub", "headers": {"Authorization": "Bearer ..."}}
172
+ }
173
+ ```
174
+
175
+ - Telegram goes through the Bot API (`sendMessage`, plus `sendDocument` for an attached file). `topics` maps forum topic names to thread ids. A message goes to the topic you name with `--topic`, else the first topic whose `routing_rules` keyword appears in the session's folder, title or the message, else `default_topic`, else the chat itself. To send through your own command instead, set `"tgnotify_bin"`. It is called as `<bin> -t <topic> [--alert] [--file F] <text>`.
176
+ - Slack posts to the channel the incoming webhook belongs to.
177
+ - `webhook` receives `{"text", "title", "session_id", "tool", "topic"}`.
178
+
179
+ The notification settings in the web workbench (the bell) show which channels are set up and send a test message.
180
+
181
+ ```bash
182
+ mixhub notify "Deploy finished" # every configured channel
183
+ mixhub notify --channel telegram --topic builds --alert "Tests failed"
184
+ mixhub notify --session 1a2b3c # a short card about that session
185
+ ```
186
+
187
+ To hear when an agent needs you or finishes a turn, add the agents' own hooks. `mixhub notify --print-hooks` prints both snippets with the full path to `mixhub`:
188
+
189
+ - Claude Code (`~/.claude/settings.json`): `Notification` and `Stop` hooks running `mixhub notify --hook claude`. You get the prompt Claude is waiting on, or the start of its last reply.
190
+ - Codex (`~/.codex/config.toml`): `notify = ["mixhub", "notify", "--hook", "codex"]`, which sends the last reply after each turn.
191
+
192
+ A hook sends to every configured channel, sends nothing when none is configured, and always exits 0.
151
193
 
152
194
  ## Language
153
195
 
@@ -162,9 +204,9 @@ The web workbench and its API listen on `127.0.0.1` only and have no login. They
162
204
  - Other programs and other users on the same machine can reach it. Don't run MixHub on a shared machine you don't trust.
163
205
  - Requests from websites in your browser are refused: the server checks the `Host` header (against DNS rebinding), `Origin` and `Sec-Fetch-Site`, only accepts JSON for anything that changes state, and sends a strict Content-Security-Policy.
164
206
  - It only answers to `127.0.0.1` and `localhost`, so a reverse proxy or a forwarded port won't work, and shouldn't. To open it from another device, use an SSH tunnel (`ssh -L 8765:127.0.0.1:8765 your-machine`, then `http://localhost:8765`).
165
- - Notification channels (Telegram, Slack, Discord, Feishu, webhooks) send session titles and message text to those services. Nothing is sent unless you configure a channel.
207
+ - Notification channels (Telegram, Slack, webhooks) send session titles and message text to those services. Nothing is sent unless you configure a channel.
166
208
 
167
- MixHub reads the agents' transcripts and databases without changing them. It writes to agent files only when you ask it to: renaming a session (the title goes where that agent keeps titles), workspace trust you opted into with `trust_all_workspaces` or `trust_roots` (so a resume doesn't stop at the trust prompt), `mixhub slim --apply` (replaces old screenshots in Claude transcripts with a placeholder) and `mixhub sync-rules` (writes `.gemini/GEMINI.md` from `AGENTS.md`).
209
+ MixHub reads the agents' transcripts and databases without changing them. It writes to agent files only when you ask it to: renaming a session (the title goes where that agent keeps titles, also while the session runs: a title record appended to a Claude transcript, a row updated in the Codex or Gemini database), workspace trust you opted into with `trust_all_workspaces` or `trust_roots` (so a resume doesn't stop at the trust prompt) and `mixhub sync-rules` (writes `.gemini/GEMINI.md` from `AGENTS.md`).
168
210
 
169
211
  ## Configuration
170
212
 
@@ -175,13 +217,13 @@ Everything works without configuration. Optional settings live in a JSON file na
175
217
  ```json
176
218
  {
177
219
  "language": "auto",
178
- "user_name": "Developer",
179
- "claude_theme": false,
220
+ "host_label": "work-laptop",
180
221
  "trust_all_workspaces": false,
181
222
  "trust_roots": ["~/workspace"],
182
223
  "agy_mouse_shim": "auto",
183
224
  "dispatch_scripts": ["my-agent-runner"],
184
- "update_check": true
225
+ "update_check": true,
226
+ "auto_sleep_hours": 0
185
227
  }
186
228
  ```
187
229
 
@@ -196,7 +238,7 @@ Everything works without configuration. Optional settings live in a JSON file na
196
238
  | `MIXHUB_NO_OPEN=1` | `mixhub web` starts the daemon without opening a browser |
197
239
  | `MIXHUB_NO_UPDATE_CHECK=1` | No daily update check |
198
240
 
199
- `dispatch_scripts` names your own wrapper scripts that start agents headlessly, so their sessions are not mistaken for interactive ones. Prompts that your own automation sends can be hidden from the list with `data/local_rules.json` (`automated_prefixes`, `automated_substrings`).
241
+ `host_label` is the machine name shown in the web workbench (the hostname by default). `dispatch_scripts` names your own wrapper scripts that start agents headlessly, so their sessions are not mistaken for interactive ones. Prompts that your own automation sends can be hidden from the list with `data/local_rules.json` (`automated_prefixes`, `automated_substrings`). Notes your own tools type into an agent on another agent's behalf go in `relayed_prefixes`: they never count as you having used a session, but don't hide one either.
200
242
 
201
243
  ## Architecture
202
244
 
@@ -208,24 +250,23 @@ Python standard library only. The code lives in the `mixhub` package:
208
250
  | `mixhub/providers` | Per-agent launch and resume commands, quota reading, native title sync, harness presets |
209
251
  | `mixhub/tui` | The curses sidebar, preview reader and window header (`HubTUI`, composed from one mixin per concern) |
210
252
  | `mixhub/tmux` | The two tmux servers: outer workspace, the `hubpool` window pool, headers, home shell |
211
- | `mixhub/handoff` | Handoff extraction and rendering, write leases, hold queue, clean forks |
212
- | `mixhub/channels` | Notification adapters, approval ledger, away receipts, Slack bridge |
253
+ | `mixhub/handoff` | Handoff extraction and rendering |
254
+ | `mixhub/channels` | One-way notifications: Telegram Bot API, Slack incoming webhook |
213
255
  | `mixhub/probe` | Reading an agent's screen: working, waiting for approval, menus |
214
256
  | `mixhub/proc` | Process identity and classification, safe-kill guards |
215
257
  | `mixhub/rules` | Shared agent rules compiler and workspace trust |
216
258
  | `mixhub/filters/agy_claude` | Optional PTY filter for AGY (theme, cursor, mouse) |
217
259
  | `mixhub/web` | Loopback HTTP API and assets for the web workbench |
218
260
  | `mixhub/cli` | The `mixhub` command |
219
- | `mixhub/core` | Shared paths, launch commands, preferences, plugin protocols |
261
+ | `mixhub/core` | Shared paths, launch commands, preferences, session states |
220
262
 
221
263
  State (pins, working set, caches, logs) lives in `~/.local/share/mixhub` for an installed package, or next to the code in a git checkout; set `MIXHUB_HOME` to put it elsewhere.
222
264
 
223
- The workspace is two tmux servers. The outer session holds the sidebar on the left and, on the right, a client of a private `tmux -L hubpool` server where every live agent runs in its own window. Switching sessions switches that window, so agents keep running untouched while you look elsewhere.
265
+ The workspace is two private tmux servers, neither of them your default one, so MixHub's sessions, options, key bindings and hooks never reach your own tmux. The outer session (`tmux -L mixhub`) holds the sidebar on the left and, on the right, a client of `tmux -L hubpool`, where every live agent runs in its own window. Switching sessions switches that window, so agents keep running untouched while you look elsewhere. Run `mixhub` inside your own tmux and the workspace opens nested in that pane.
224
266
 
225
267
  ## Roadmap
226
268
 
227
269
  - An agent-facing command set (list sessions, read another session's recent turns) and a skill so agents can use MixHub themselves.
228
- - Two-way chat channels: replies in a Telegram topic delivered into the matching session.
229
270
  - Broader Linux polish.
230
271
 
231
272
  ## Development
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env python3
2
- """Idempotent launcher for MixHub / Agent Session Hub (TUI, tmux workspace, handoff, sync-rules, notify, mobile-handoff, install-icons, and Web daemon).
2
+ """Idempotent launcher for MixHub / Agent Session Hub (TUI, tmux workspace, handoff, sync-rules, notify, and Web daemon).
3
3
 
4
4
  Thin entry point: the dispatcher and every command live in `mixhub.cli` (`main`, `daemon`, `sessions`, `tools`, `workspace`).
5
5
  """