mixhub-cli 0.2.0__tar.gz → 0.2.2__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 (344) hide show
  1. mixhub_cli-0.2.2/.github/assets/terminal.png +0 -0
  2. mixhub_cli-0.2.2/.github/assets/web-dark.png +0 -0
  3. mixhub_cli-0.2.2/.github/assets/web-light.png +0 -0
  4. mixhub_cli-0.2.2/.github/workflows/publish.yml +28 -0
  5. mixhub_cli-0.2.2/.github/workflows/test.yml +34 -0
  6. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/.gitignore +3 -0
  7. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/PKG-INFO +96 -53
  8. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/Preferences.example +3 -3
  9. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/README.md +94 -51
  10. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/bin/hub +1 -1
  11. mixhub_cli-0.2.2/mixhub/__init__.py +12 -0
  12. mixhub_cli-0.2.2/mixhub/channels/__init__.py +55 -0
  13. mixhub_cli-0.2.2/mixhub/channels/base.py +35 -0
  14. mixhub_cli-0.2.2/mixhub/channels/cli.py +92 -0
  15. mixhub_cli-0.2.2/mixhub/channels/config.py +165 -0
  16. mixhub_cli-0.2.2/mixhub/channels/hooks.py +92 -0
  17. mixhub_cli-0.2.2/mixhub/channels/manager.py +73 -0
  18. mixhub_cli-0.2.2/mixhub/channels/slack.py +57 -0
  19. mixhub_cli-0.2.2/mixhub/channels/telegram.py +263 -0
  20. mixhub_cli-0.2.2/mixhub/channels/text.py +38 -0
  21. mixhub_cli-0.2.2/mixhub/channels/webhook.py +50 -0
  22. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/__init__.py +2 -2
  23. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/main.py +13 -42
  24. mixhub_cli-0.2.2/mixhub/cli/sessions.py +35 -0
  25. mixhub_cli-0.2.2/mixhub/cli/tools.py +19 -0
  26. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/update.py +2 -1
  27. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/workspace.py +53 -17
  28. mixhub_cli-0.2.2/mixhub/core/__init__.py +78 -0
  29. mixhub_cli-0.2.2/mixhub/core/browser.py +40 -0
  30. mixhub_cli-0.2.2/mixhub/core/cli_version.py +75 -0
  31. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/launch.py +9 -11
  32. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/paths.py +0 -14
  33. mixhub_cli-0.2.2/mixhub/core/prefs.py +61 -0
  34. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/state.py +0 -24
  35. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/textwidth.py +14 -10
  36. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/__init__.py +2 -2
  37. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/main.py +6 -13
  38. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/prefs.py +2 -33
  39. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/stream.py +5 -38
  40. mixhub_cli-0.2.2/mixhub/handoff/__init__.py +60 -0
  41. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/bundle.py +1 -53
  42. mixhub_cli-0.2.2/mixhub/handoff/disposition.py +23 -0
  43. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/extract.py +0 -13
  44. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/relay.py +19 -85
  45. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/__init__.py +4 -15
  46. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/actions.py +23 -2
  47. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/discovery.py +140 -18
  48. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/hub.py +2 -0
  49. mixhub_cli-0.2.2/mixhub/hub/integrations.py +100 -0
  50. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/lifecycle.py +27 -176
  51. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/pane_probe.py +10 -0
  52. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/processes.py +35 -1
  53. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/schema.py +4 -0
  54. mixhub_cli-0.2.2/mixhub/hub/send.py +142 -0
  55. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/session_filters.py +63 -44
  56. mixhub_cli-0.2.2/mixhub/hub/shell.py +85 -0
  57. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/sleep_blockers.py +5 -5
  58. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/summary.py +11 -197
  59. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/util.py +3 -2
  60. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/working_set.py +0 -1
  61. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/i18n/__init__.py +5 -5
  62. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/i18n/en.json +41 -34
  63. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/i18n/ja.json +42 -35
  64. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/i18n/zh.json +50 -43
  65. mixhub_cli-0.2.2/mixhub/ops/__init__.py +8 -0
  66. mixhub_cli-0.2.2/mixhub/ops/doctor.py +156 -0
  67. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/__init__.py +2 -4
  68. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/capture.py +0 -11
  69. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/classify.py +2 -1
  70. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/patterns.py +4 -0
  71. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/classify.py +2 -3
  72. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/identity.py +13 -1
  73. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/__init__.py +1 -63
  74. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/claude.py +1 -3
  75. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/jsonl.py +1 -1
  76. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/launch.py +0 -30
  77. mixhub_cli-0.2.2/mixhub/providers/preferences.py +13 -0
  78. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/presets.py +1 -37
  79. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/quota.py +8 -3
  80. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/readiness.py +10 -107
  81. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/statusline.py +16 -8
  82. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/title_overrides.py +16 -73
  83. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/rules/__init__.py +0 -1
  84. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/rules/sync.py +1 -1
  85. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/rules/trust.py +5 -38
  86. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/__init__.py +1 -1
  87. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/gateway.py +17 -51
  88. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/headers.py +1 -1
  89. mixhub_cli-0.2.2/mixhub/tmux/home_shell.py +141 -0
  90. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/new_sessions.py +122 -11
  91. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/onexit.py +48 -38
  92. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/outer.py +20 -18
  93. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/pool.py +29 -9
  94. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/session_state.py +22 -10
  95. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/workspace.py +40 -74
  96. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/__init__.py +1 -4
  97. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/__main__.py +1 -1
  98. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/app.py +20 -54
  99. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/badges.py +3 -49
  100. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/style.py +6 -22
  101. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/drafts.py +35 -2
  102. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/__init__.py +2 -2
  103. mixhub_cli-0.2.2/mixhub/tui/hubtui/actions.py +261 -0
  104. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/attention.py +15 -13
  105. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/colors.py +6 -6
  106. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/draw.py +7 -10
  107. mixhub_cli-0.2.2/mixhub/tui/hubtui/evq.py +99 -0
  108. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/home.py +43 -23
  109. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/layout.py +8 -21
  110. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/loop.py +31 -190
  111. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/mouse.py +0 -28
  112. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/popovers.py +4 -19
  113. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/reader.py +11 -36
  114. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/relay.py +3 -63
  115. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/selection_sync.py +32 -4
  116. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/sidebar.py +28 -36
  117. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/wordmark_row.py +1 -1
  118. mixhub_cli-0.2.2/mixhub/tui/settings.py +9 -0
  119. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/web.py +2 -17
  120. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/__init__.py +0 -6
  121. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/popover.py +2 -51
  122. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/quota_table.py +1 -52
  123. mixhub_cli-0.2.2/mixhub/tui/widgets/toast.py +17 -0
  124. mixhub_cli-0.2.2/mixhub/tui/widgets/wordmark.py +203 -0
  125. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/handler.py +1 -18
  126. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/static/app.js +60 -49
  127. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/static/index.html +25 -26
  128. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/static/style.css +9 -5
  129. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/pyproject.toml +1 -1
  130. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/scripts/demo_seed.py +95 -7
  131. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/agy_claude_filter.py +3 -11
  132. mixhub_cli-0.2.2/tests/compat/channels.py +28 -0
  133. mixhub_cli-0.2.2/tests/compat/handoff.py +56 -0
  134. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/probe.py +1 -9
  135. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/providers.py +0 -67
  136. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/rules_sync.py +2 -10
  137. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/server.py +1 -2
  138. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/tui.py +1 -4
  139. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/wordmark.py +7 -8
  140. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_agy_filter_package.py +2 -3
  141. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_agy_headless_and_hibernate_state.py +18 -26
  142. mixhub_cli-0.2.2/tests/test_agy_headless_continued.py +157 -0
  143. mixhub_cli-0.2.2/tests/test_auto_sleep_pref.py +86 -0
  144. mixhub_cli-0.2.2/tests/test_channels_config.py +108 -0
  145. mixhub_cli-0.2.2/tests/test_consumer_checks.py +31 -0
  146. mixhub_cli-0.2.2/tests/test_demo_seed.py +95 -0
  147. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_e2e_ux_audit.py +522 -411
  148. mixhub_cli-0.2.2/tests/test_first_run.py +52 -0
  149. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_handoff_hold_and_fork.py +0 -331
  150. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_harness_presets.py +8 -203
  151. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_hub.py +292 -431
  152. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_hub_providers_layout.py +2 -2
  153. mixhub_cli-0.2.2/tests/test_hubpool_duplicate_windows.py +74 -0
  154. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_i18n.py +9 -40
  155. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_launch_forms.py +13 -22
  156. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_modular_and_trust.py +9 -36
  157. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_modular_channels_handoff.py +8 -108
  158. mixhub_cli-0.2.2/tests/test_no_agent_file_deletion.py +79 -0
  159. mixhub_cli-0.2.2/tests/test_notify_hooks.py +117 -0
  160. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_oss_desensitize_and_cc_ds.py +32 -320
  161. mixhub_cli-0.2.2/tests/test_own_tmux_server.py +126 -0
  162. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_package_import_rule.py +5 -11
  163. mixhub_cli-0.2.2/tests/test_platform_support.py +307 -0
  164. mixhub_cli-0.2.2/tests/test_preferences_lookup.py +67 -0
  165. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_probe.py +27 -125
  166. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_server.py +44 -0
  167. mixhub_cli-0.2.2/tests/test_session_hygiene.py +316 -0
  168. mixhub_cli-0.2.2/tests/test_sidebar_pinned_order.py +22 -0
  169. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_state_root.py +17 -21
  170. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_telegram_bot_api_send.py +2 -4
  171. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_tui_core_primitives.py +12 -10
  172. mixhub_cli-0.2.2/tests/test_windows_compat.py +103 -0
  173. mixhub_cli-0.2.2/tests/test_wordmark.py +87 -0
  174. mixhub_cli-0.2.2/tests/test_working_set_removal_log.py +32 -0
  175. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_zoom_toggle_and_badge.py +22 -0
  176. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/webui_composer.test.mjs +162 -21
  177. mixhub_cli-0.2.0/.github/assets/web-dark.png +0 -0
  178. mixhub_cli-0.2.0/.github/assets/web-light.png +0 -0
  179. mixhub_cli-0.2.0/mixhub/__init__.py +0 -52
  180. mixhub_cli-0.2.0/mixhub/channels/__init__.py +0 -144
  181. mixhub_cli-0.2.0/mixhub/channels/approvals.py +0 -367
  182. mixhub_cli-0.2.0/mixhub/channels/attention.py +0 -814
  183. mixhub_cli-0.2.0/mixhub/channels/base.py +0 -69
  184. mixhub_cli-0.2.0/mixhub/channels/cards.py +0 -525
  185. mixhub_cli-0.2.0/mixhub/channels/cli.py +0 -74
  186. mixhub_cli-0.2.0/mixhub/channels/config.py +0 -196
  187. mixhub_cli-0.2.0/mixhub/channels/discord.py +0 -396
  188. mixhub_cli-0.2.0/mixhub/channels/feishu.py +0 -293
  189. mixhub_cli-0.2.0/mixhub/channels/hooks.py +0 -48
  190. mixhub_cli-0.2.0/mixhub/channels/language.py +0 -77
  191. mixhub_cli-0.2.0/mixhub/channels/manager.py +0 -630
  192. mixhub_cli-0.2.0/mixhub/channels/poller_lock.py +0 -132
  193. mixhub_cli-0.2.0/mixhub/channels/receipts.py +0 -365
  194. mixhub_cli-0.2.0/mixhub/channels/router.py +0 -145
  195. mixhub_cli-0.2.0/mixhub/channels/slack.py +0 -320
  196. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/__init__.py +0 -78
  197. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/__main__.py +0 -12
  198. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/approvals.py +0 -263
  199. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/bridge.py +0 -1079
  200. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/config.py +0 -95
  201. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/daemon.py +0 -165
  202. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/delivery.py +0 -227
  203. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/web_api.py +0 -234
  204. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/websocket.py +0 -198
  205. mixhub_cli-0.2.0/mixhub/channels/store.py +0 -430
  206. mixhub_cli-0.2.0/mixhub/channels/telegram.py +0 -1400
  207. mixhub_cli-0.2.0/mixhub/channels/text.py +0 -256
  208. mixhub_cli-0.2.0/mixhub/cli/sessions.py +0 -93
  209. mixhub_cli-0.2.0/mixhub/cli/tools.py +0 -42
  210. mixhub_cli-0.2.0/mixhub/core/__init__.py +0 -159
  211. mixhub_cli-0.2.0/mixhub/core/adapters.py +0 -145
  212. mixhub_cli-0.2.0/mixhub/core/models.py +0 -106
  213. mixhub_cli-0.2.0/mixhub/core/prefs.py +0 -86
  214. mixhub_cli-0.2.0/mixhub/core/protocols.py +0 -122
  215. mixhub_cli-0.2.0/mixhub/core/registry.py +0 -265
  216. mixhub_cli-0.2.0/mixhub/handoff/__init__.py +0 -257
  217. mixhub_cli-0.2.0/mixhub/handoff/branch.py +0 -40
  218. mixhub_cli-0.2.0/mixhub/handoff/common.py +0 -308
  219. mixhub_cli-0.2.0/mixhub/handoff/disposition.py +0 -83
  220. mixhub_cli-0.2.0/mixhub/handoff/fence.py +0 -84
  221. mixhub_cli-0.2.0/mixhub/handoff/fork.py +0 -522
  222. mixhub_cli-0.2.0/mixhub/handoff/holdqueue.py +0 -497
  223. mixhub_cli-0.2.0/mixhub/handoff/receipt.py +0 -68
  224. mixhub_cli-0.2.0/mixhub/handoff/store.py +0 -286
  225. mixhub_cli-0.2.0/mixhub/hub/integrations.py +0 -351
  226. mixhub_cli-0.2.0/mixhub/hub/shell.py +0 -49
  227. mixhub_cli-0.2.0/mixhub/ops/__init__.py +0 -8
  228. mixhub_cli-0.2.0/mixhub/ops/cost_report.py +0 -629
  229. mixhub_cli-0.2.0/mixhub/ops/doctor.py +0 -27
  230. mixhub_cli-0.2.0/mixhub/ops/icons_font.py +0 -218
  231. mixhub_cli-0.2.0/mixhub/probe/away.py +0 -218
  232. mixhub_cli-0.2.0/mixhub/providers/adapters.py +0 -139
  233. mixhub_cli-0.2.0/mixhub/providers/grok.py +0 -283
  234. mixhub_cli-0.2.0/mixhub/providers/preferences.py +0 -37
  235. mixhub_cli-0.2.0/mixhub/quirks/__init__.py +0 -118
  236. mixhub_cli-0.2.0/mixhub/rules/titles.py +0 -28
  237. mixhub_cli-0.2.0/mixhub/surfaces/__init__.py +0 -71
  238. mixhub_cli-0.2.0/mixhub/tmux/home_shell.py +0 -62
  239. mixhub_cli-0.2.0/mixhub/tui/hubtui/actions.py +0 -453
  240. mixhub_cli-0.2.0/mixhub/tui/onboarding.py +0 -118
  241. mixhub_cli-0.2.0/mixhub/tui/settings.py +0 -34
  242. mixhub_cli-0.2.0/mixhub/tui/widgets/toast.py +0 -21
  243. mixhub_cli-0.2.0/mixhub/tui/widgets/welcome.py +0 -7
  244. mixhub_cli-0.2.0/mixhub/tui/widgets/wordmark.py +0 -348
  245. mixhub_cli-0.2.0/tests/compat/channels.py +0 -129
  246. mixhub_cli-0.2.0/tests/compat/cost_report.py +0 -26
  247. mixhub_cli-0.2.0/tests/compat/handoff.py +0 -126
  248. mixhub_cli-0.2.0/tests/compat/icons_font.py +0 -24
  249. mixhub_cli-0.2.0/tests/compat/protocols.py +0 -59
  250. mixhub_cli-0.2.0/tests/compat/slack_bridge.py +0 -62
  251. mixhub_cli-0.2.0/tests/test_anti_context_rot_primitives.py +0 -289
  252. mixhub_cli-0.2.0/tests/test_away_receipt_zero_emoji.py +0 -1054
  253. mixhub_cli-0.2.0/tests/test_demo_seed.py +0 -49
  254. mixhub_cli-0.2.0/tests/test_handoff_ab.py +0 -99
  255. mixhub_cli-0.2.0/tests/test_platform_support.py +0 -159
  256. mixhub_cli-0.2.0/tests/test_slack_bridge.py +0 -940
  257. mixhub_cli-0.2.0/tests/test_toml_fallback.py +0 -34
  258. mixhub_cli-0.2.0/tests/test_windows_compat.py +0 -247
  259. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/LICENSE +0 -0
  260. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/bin/mixhub +0 -0
  261. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/install.sh +0 -0
  262. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/__main__.py +0 -0
  263. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/daemon.py +0 -0
  264. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/env.py +0 -0
  265. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/__init__.py +0 -0
  266. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/__main__.py +0 -0
  267. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/cursor.py +0 -0
  268. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/debuglog.py +0 -0
  269. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/mouse.py +0 -0
  270. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/patterns.py +0 -0
  271. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/vscreen.py +0 -0
  272. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/contract.py +0 -0
  273. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/resume.py +0 -0
  274. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/text.py +0 -0
  275. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/constants.py +0 -0
  276. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/index.py +0 -0
  277. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/listing.py +0 -0
  278. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/metrics.py +0 -0
  279. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/opencode.py +0 -0
  280. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/text.py +0 -0
  281. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/titles.py +0 -0
  282. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/transcript.py +0 -0
  283. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/menus.py +0 -0
  284. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/rules.py +0 -0
  285. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/text.py +0 -0
  286. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/__init__.py +0 -0
  287. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/guard.py +0 -0
  288. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/tree.py +0 -0
  289. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/deepseek.py +0 -0
  290. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/native_titles.py +0 -0
  291. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/resume.py +0 -0
  292. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/rules/blocks.py +0 -0
  293. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/commands.py +0 -0
  294. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/caffeinate.py +0 -0
  295. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/__init__.py +0 -0
  296. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/canvas.py +0 -0
  297. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/events.py +0 -0
  298. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/geometry.py +0 -0
  299. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/hitmap.py +0 -0
  300. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/input.py +0 -0
  301. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/loop.py +0 -0
  302. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/widget.py +0 -0
  303. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/diag.py +0 -0
  304. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/footer.py +0 -0
  305. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/header.py +0 -0
  306. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/navigation.py +0 -0
  307. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/prompt.py +0 -0
  308. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/refresh.py +0 -0
  309. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/paths.py +0 -0
  310. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/quota.py +0 -0
  311. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/selection.py +0 -0
  312. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/sidebar_rows.py +0 -0
  313. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/chip.py +0 -0
  314. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/divider.py +0 -0
  315. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/header_bar.py +0 -0
  316. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/preview_bar.py +0 -0
  317. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/sidebar_row.py +0 -0
  318. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/__init__.py +0 -0
  319. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/__main__.py +0 -0
  320. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/server.py +0 -0
  321. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/browser_check.mjs +0 -0
  322. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/README.md +0 -0
  323. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/mixhub_cli.py +0 -0
  324. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/proc_guard.py +0 -0
  325. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/conftest.py +0 -0
  326. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/fixtures/agy_1_4_7_altscreen_typed_120x40.bin +0 -0
  327. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/fixtures/agy_1_4_7_altscreen_wrap_paste_40x30.bin +0 -0
  328. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/mcp_client.mjs +0 -0
  329. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_agy_filter_mouse.py +0 -0
  330. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_bg_proc_scan.py +0 -0
  331. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_caffeinate.py +0 -0
  332. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_env_names.py +0 -0
  333. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_opencode.py +0 -0
  334. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_packaging.py +0 -0
  335. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_proc_guard.py +0 -0
  336. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_providers_live.py +0 -0
  337. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_rules_sync.py +0 -0
  338. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_schema_resilience.py +0 -0
  339. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_statusline_command.py +0 -0
  340. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_subagent_filter.py +0 -0
  341. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_test_isolation.py +0 -0
  342. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_tui_module_layout.py +0 -0
  343. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_update.py +0 -0
  344. {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/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.2
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,54 @@ 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
+ | | Other agents | Runs in | Status | Outside sessions |
68
+ | :--- | :--- | :--- | :--- | :--- |
69
+ | MixHub | Codex, Gemini, OpenCode | tmux | session files | listed |
70
+ | [Agent View](https://code.claude.com/docs/en/agent-view) | none | `claude` CLI | yes | after `/bg` |
71
+ | [cmux](https://github.com/manaflow-ai/cmux) | Codex, Gemini, OpenCode, … | macOS app | hooks / OSC | – |
72
+ | [claude-squad](https://github.com/smtg-ai/claude-squad) | Codex, Gemini, Aider | tmux | – | no |
73
+ | [ccmanager](https://github.com/kbwo/ccmanager) | Codex, Gemini, OpenCode, … | any terminal | yes | – |
74
+ | [agent-deck](https://github.com/asheshgoplani/agent-deck) | Codex, Gemini, OpenCode, … | tmux | yes | no |
75
+ | [tmux-agent-status](https://github.com/samleeney/tmux-agent-status) | Codex, Devin | your tmux | hooks | if in tmux |
76
+
77
+ – means the project's docs don't say.
78
+
79
+ If you only use Claude Code, Agent View is built in. If you want a git worktree per task, claude-squad and ccmanager make one for you (agent-deck optionally); MixHub doesn't touch git.
69
80
 
70
81
  ## Install
71
82
 
@@ -86,7 +97,7 @@ The script installs the `mixhub-cli` package (it provides the `mixhub` command)
86
97
  ```bash
87
98
  mixhub # open or reattach to the workspace
88
99
  mixhub web # local web workbench at http://127.0.0.1:8765
89
- mixhub doctor # check providers, channels, locks and terminal settings
100
+ mixhub doctor # check agent CLIs, tmux, notification channels and terminal settings
90
101
  ```
91
102
 
92
103
  From a clone:
@@ -114,17 +125,17 @@ mixhub update --check # only say whether a newer version exists
114
125
  | Codex | `~/.codex/sessions/` rollouts + state DB | `codex resume <id>` |
115
126
  | OpenCode | `opencode.db` (read-only) | `opencode -s <id>` |
116
127
 
117
- `✎ New session` can also start Claude Code with another model behind it (DeepSeek, Kimi, Qwen and GLM presets); the badge shows which model a session uses.
128
+ `✎ New session` opens the new session in the working directory of the session selected in the sidebar, so to start in a project folder, click one of that folder's sessions first. With nothing selected, including the `LAUNCH` buttons on Home, it uses the folder you ran `mixhub` from. It can also start Claude Code with another model behind it (DeepSeek, Kimi, Qwen and GLM presets); the badge shows which model a session uses.
118
129
 
119
130
  ## The workspace
120
131
 
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.
132
+ **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
133
 
123
134
  **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
135
 
125
- **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).
136
+ **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). An agent started there (`claude`, `codex`, `opencode`, `agy`) moves to its own window in `Active`, the same as `✎ New session`, and Home gets a fresh shell; one-shot runs like `claude -p` stay in the shell.
126
137
 
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:
138
+ **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
139
 
129
140
  ```jsonc
130
141
  // ~/.claude/settings.json
@@ -144,15 +155,11 @@ Without it the footer estimates Claude's quota from transcripts.
144
155
 
145
156
  ## Session lifecycle
146
157
 
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:
158
+ 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
159
 
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 |
160
+ 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
161
 
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.
162
+ 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
163
 
157
164
  ## Handoff between agents
158
165
 
@@ -164,7 +171,7 @@ mixhub handoff SESSION_ID --to claude --mode review
164
171
  mixhub handoff SESSION_ID --to agy --dry-run
165
172
  ```
166
173
 
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.
174
+ The new session is titled `↳ <source title>`.
168
175
 
169
176
  ## Web workbench and notifications
170
177
 
@@ -173,8 +180,45 @@ The new session is titled `↳ <source title>`. A write lease per working tree r
173
180
  <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
181
  </picture>
175
182
 
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.
183
+ - `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.
184
+ - `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.
185
+
186
+ ### Notification channels
187
+
188
+ Nothing is sent until you set up a channel in `~/.config/mixhub/channels.json`. Every section is optional:
189
+
190
+ ```json
191
+ {
192
+ "telegram": {
193
+ "bot_token": "123456:ABC...",
194
+ "chat_id": -1001234567890,
195
+ "topics": {"builds": 42, "reviews": 57},
196
+ "default_topic": "builds",
197
+ "routing_rules": {"reviews": ["review", "pr"]}
198
+ },
199
+ "slack": {"webhook_url": "https://hooks.slack.com/services/..."},
200
+ "webhook": {"url": "https://example.com/mixhub", "headers": {"Authorization": "Bearer ..."}}
201
+ }
202
+ ```
203
+
204
+ - 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>`.
205
+ - Slack posts to the channel the incoming webhook belongs to.
206
+ - `webhook` receives `{"text", "title", "session_id", "tool", "topic"}`.
207
+
208
+ The notification settings in the web workbench (the bell) show which channels are set up and send a test message.
209
+
210
+ ```bash
211
+ mixhub notify "Deploy finished" # every configured channel
212
+ mixhub notify --channel telegram --topic builds --alert "Tests failed"
213
+ mixhub notify --session 1a2b3c # a short card about that session
214
+ ```
215
+
216
+ 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`:
217
+
218
+ - 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.
219
+ - Codex (`~/.codex/config.toml`): `notify = ["mixhub", "notify", "--hook", "codex"]`, which sends the last reply after each turn.
220
+
221
+ A hook sends to every configured channel, sends nothing when none is configured, and always exits 0.
178
222
 
179
223
  ## Language
180
224
 
@@ -189,9 +233,9 @@ The web workbench and its API listen on `127.0.0.1` only and have no login. They
189
233
  - 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
234
  - 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
235
  - 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.
236
+ - Notification channels (Telegram, Slack, webhooks) send session titles and message text to those services. Nothing is sent unless you configure a channel.
193
237
 
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`).
238
+ 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
239
 
196
240
  ## Configuration
197
241
 
@@ -202,13 +246,13 @@ Everything works without configuration. Optional settings live in a JSON file na
202
246
  ```json
203
247
  {
204
248
  "language": "auto",
205
- "user_name": "Developer",
206
- "claude_theme": false,
249
+ "host_label": "work-laptop",
207
250
  "trust_all_workspaces": false,
208
251
  "trust_roots": ["~/workspace"],
209
252
  "agy_mouse_shim": "auto",
210
253
  "dispatch_scripts": ["my-agent-runner"],
211
- "update_check": true
254
+ "update_check": true,
255
+ "auto_sleep_hours": 0
212
256
  }
213
257
  ```
214
258
 
@@ -223,7 +267,7 @@ Everything works without configuration. Optional settings live in a JSON file na
223
267
  | `MIXHUB_NO_OPEN=1` | `mixhub web` starts the daemon without opening a browser |
224
268
  | `MIXHUB_NO_UPDATE_CHECK=1` | No daily update check |
225
269
 
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`).
270
+ `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. Sessions another agent started are hidden by what their CLI records (subagents, headless runs, Codex tasks whose `originator` is Claude Code). For older sessions without such a record, prompts that your own automation sends can be hidden with `data/local_rules.json` (`automated_prefixes`, `automated_substrings`, and `codex_automated_prefixes` for Codex only); `mixhub doctor --hidden` lists every session a text rule hides. 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
271
 
228
272
  ## Architecture
229
273
 
@@ -235,24 +279,23 @@ Python standard library only. The code lives in the `mixhub` package:
235
279
  | `mixhub/providers` | Per-agent launch and resume commands, quota reading, native title sync, harness presets |
236
280
  | `mixhub/tui` | The curses sidebar, preview reader and window header (`HubTUI`, composed from one mixin per concern) |
237
281
  | `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 |
282
+ | `mixhub/handoff` | Handoff extraction and rendering |
283
+ | `mixhub/channels` | One-way notifications: Telegram Bot API, Slack incoming webhook |
240
284
  | `mixhub/probe` | Reading an agent's screen: working, waiting for approval, menus |
241
285
  | `mixhub/proc` | Process identity and classification, safe-kill guards |
242
286
  | `mixhub/rules` | Shared agent rules compiler and workspace trust |
243
287
  | `mixhub/filters/agy_claude` | Optional PTY filter for AGY (theme, cursor, mouse) |
244
288
  | `mixhub/web` | Loopback HTTP API and assets for the web workbench |
245
289
  | `mixhub/cli` | The `mixhub` command |
246
- | `mixhub/core` | Shared paths, launch commands, preferences, plugin protocols |
290
+ | `mixhub/core` | Shared paths, launch commands, preferences, session states |
247
291
 
248
292
  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
293
 
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.
294
+ 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
295
 
252
296
  ## Roadmap
253
297
 
254
298
  - 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
299
  - Broader Linux polish.
257
300
 
258
301
  ## 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
  }