mixhub-cli 0.1.0__tar.gz → 0.2.0__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 (308) hide show
  1. mixhub_cli-0.2.0/.github/assets/web-dark.png +0 -0
  2. mixhub_cli-0.2.0/.github/assets/web-light.png +0 -0
  3. mixhub_cli-0.2.0/.gitignore +60 -0
  4. mixhub_cli-0.2.0/LICENSE +21 -0
  5. mixhub_cli-0.2.0/PKG-INFO +270 -0
  6. mixhub_cli-0.2.0/Preferences.example +10 -0
  7. mixhub_cli-0.2.0/README.md +243 -0
  8. mixhub_cli-0.2.0/bin/hub +21 -0
  9. mixhub_cli-0.2.0/install.sh +98 -0
  10. mixhub_cli-0.2.0/mixhub/__init__.py +52 -0
  11. mixhub_cli-0.2.0/mixhub/__main__.py +12 -0
  12. mixhub_cli-0.2.0/mixhub/channels/__init__.py +144 -0
  13. mixhub_cli-0.2.0/mixhub/channels/approvals.py +367 -0
  14. mixhub_cli-0.2.0/mixhub/channels/attention.py +814 -0
  15. mixhub_cli-0.2.0/mixhub/channels/base.py +69 -0
  16. mixhub_cli-0.2.0/mixhub/channels/cards.py +525 -0
  17. mixhub_cli-0.2.0/mixhub/channels/cli.py +74 -0
  18. mixhub_cli-0.2.0/mixhub/channels/config.py +196 -0
  19. mixhub_cli-0.2.0/mixhub/channels/discord.py +396 -0
  20. mixhub_cli-0.2.0/mixhub/channels/feishu.py +293 -0
  21. mixhub_cli-0.2.0/mixhub/channels/hooks.py +48 -0
  22. mixhub_cli-0.2.0/mixhub/channels/language.py +77 -0
  23. mixhub_cli-0.2.0/mixhub/channels/manager.py +630 -0
  24. mixhub_cli-0.2.0/mixhub/channels/poller_lock.py +132 -0
  25. mixhub_cli-0.2.0/mixhub/channels/receipts.py +365 -0
  26. mixhub_cli-0.2.0/mixhub/channels/router.py +145 -0
  27. mixhub_cli-0.2.0/mixhub/channels/slack.py +320 -0
  28. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/__init__.py +78 -0
  29. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/__main__.py +12 -0
  30. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/approvals.py +263 -0
  31. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/bridge.py +1079 -0
  32. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/config.py +95 -0
  33. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/daemon.py +165 -0
  34. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/delivery.py +227 -0
  35. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/web_api.py +234 -0
  36. mixhub_cli-0.2.0/mixhub/channels/slack_bridge/websocket.py +198 -0
  37. mixhub_cli-0.2.0/mixhub/channels/store.py +430 -0
  38. mixhub_cli-0.2.0/mixhub/channels/telegram.py +1400 -0
  39. mixhub_cli-0.2.0/mixhub/channels/text.py +256 -0
  40. mixhub_cli-0.2.0/mixhub/cli/__init__.py +11 -0
  41. mixhub_cli-0.2.0/mixhub/cli/daemon.py +142 -0
  42. mixhub_cli-0.2.0/mixhub/cli/main.py +179 -0
  43. mixhub_cli-0.2.0/mixhub/cli/sessions.py +93 -0
  44. mixhub_cli-0.2.0/mixhub/cli/tools.py +42 -0
  45. mixhub_cli-0.2.0/mixhub/cli/update.py +211 -0
  46. mixhub_cli-0.2.0/mixhub/cli/workspace.py +269 -0
  47. mixhub_cli-0.2.0/mixhub/core/__init__.py +159 -0
  48. mixhub_cli-0.2.0/mixhub/core/adapters.py +145 -0
  49. mixhub_cli-0.2.0/mixhub/core/env.py +17 -0
  50. mixhub_cli-0.2.0/mixhub/core/launch.py +236 -0
  51. mixhub_cli-0.2.0/mixhub/core/models.py +106 -0
  52. mixhub_cli-0.2.0/mixhub/core/paths.py +112 -0
  53. mixhub_cli-0.2.0/mixhub/core/prefs.py +86 -0
  54. mixhub_cli-0.2.0/mixhub/core/protocols.py +122 -0
  55. mixhub_cli-0.2.0/mixhub/core/registry.py +265 -0
  56. mixhub_cli-0.2.0/mixhub/core/state.py +108 -0
  57. mixhub_cli-0.2.0/mixhub/core/textwidth.py +164 -0
  58. mixhub_cli-0.2.0/mixhub/filters/__init__.py +2 -0
  59. mixhub_cli-0.2.0/mixhub/filters/agy_claude/__init__.py +18 -0
  60. mixhub_cli-0.2.0/mixhub/filters/agy_claude/__main__.py +12 -0
  61. mixhub_cli-0.1.0/agy_claude_filter.py → mixhub_cli-0.2.0/mixhub/filters/agy_claude/cursor.py +556 -419
  62. mixhub_cli-0.2.0/mixhub/filters/agy_claude/debuglog.py +61 -0
  63. mixhub_cli-0.2.0/mixhub/filters/agy_claude/main.py +285 -0
  64. mixhub_cli-0.2.0/mixhub/filters/agy_claude/mouse.py +198 -0
  65. mixhub_cli-0.2.0/mixhub/filters/agy_claude/patterns.py +39 -0
  66. mixhub_cli-0.2.0/mixhub/filters/agy_claude/prefs.py +78 -0
  67. mixhub_cli-0.2.0/mixhub/filters/agy_claude/stream.py +319 -0
  68. mixhub_cli-0.2.0/mixhub/filters/agy_claude/vscreen.py +527 -0
  69. mixhub_cli-0.2.0/mixhub/handoff/__init__.py +257 -0
  70. mixhub_cli-0.2.0/mixhub/handoff/branch.py +40 -0
  71. mixhub_cli-0.2.0/mixhub/handoff/bundle.py +281 -0
  72. mixhub_cli-0.2.0/mixhub/handoff/common.py +308 -0
  73. mixhub_cli-0.2.0/mixhub/handoff/contract.py +98 -0
  74. mixhub_cli-0.2.0/mixhub/handoff/disposition.py +83 -0
  75. mixhub_cli-0.2.0/mixhub/handoff/extract.py +1226 -0
  76. mixhub_cli-0.2.0/mixhub/handoff/fence.py +84 -0
  77. mixhub_cli-0.2.0/mixhub/handoff/fork.py +522 -0
  78. mixhub_cli-0.2.0/mixhub/handoff/holdqueue.py +497 -0
  79. mixhub_cli-0.2.0/mixhub/handoff/receipt.py +68 -0
  80. mixhub_cli-0.2.0/mixhub/handoff/relay.py +340 -0
  81. mixhub_cli-0.2.0/mixhub/handoff/resume.py +228 -0
  82. mixhub_cli-0.2.0/mixhub/handoff/store.py +286 -0
  83. mixhub_cli-0.2.0/mixhub/handoff/text.py +226 -0
  84. mixhub_cli-0.2.0/mixhub/hub/__init__.py +166 -0
  85. mixhub_cli-0.2.0/mixhub/hub/actions.py +192 -0
  86. mixhub_cli-0.2.0/mixhub/hub/constants.py +42 -0
  87. mixhub_cli-0.2.0/mixhub/hub/discovery.py +1141 -0
  88. mixhub_cli-0.2.0/mixhub/hub/hub.py +87 -0
  89. mixhub_cli-0.2.0/mixhub/hub/index.py +71 -0
  90. mixhub_cli-0.2.0/mixhub/hub/integrations.py +351 -0
  91. mixhub_cli-0.2.0/mixhub/hub/lifecycle.py +351 -0
  92. mixhub_cli-0.2.0/mixhub/hub/listing.py +341 -0
  93. mixhub_cli-0.2.0/mixhub/hub/metrics.py +241 -0
  94. mixhub_cli-0.2.0/mixhub/hub/opencode.py +90 -0
  95. mixhub_cli-0.2.0/mixhub/hub/pane_probe.py +188 -0
  96. mixhub_cli-0.2.0/mixhub/hub/processes.py +337 -0
  97. mixhub_cli-0.2.0/mixhub/hub/schema.py +88 -0
  98. mixhub_cli-0.2.0/mixhub/hub/session_filters.py +183 -0
  99. mixhub_cli-0.2.0/mixhub/hub/shell.py +49 -0
  100. mixhub_cli-0.2.0/mixhub/hub/sleep_blockers.py +785 -0
  101. mixhub_cli-0.2.0/mixhub/hub/summary.py +605 -0
  102. mixhub_cli-0.2.0/mixhub/hub/text.py +73 -0
  103. mixhub_cli-0.2.0/mixhub/hub/titles.py +126 -0
  104. mixhub_cli-0.2.0/mixhub/hub/transcript.py +215 -0
  105. mixhub_cli-0.2.0/mixhub/hub/util.py +46 -0
  106. mixhub_cli-0.2.0/mixhub/hub/working_set.py +166 -0
  107. mixhub_cli-0.2.0/mixhub/i18n/__init__.py +86 -0
  108. mixhub_cli-0.2.0/mixhub/i18n/en.json +221 -0
  109. mixhub_cli-0.2.0/mixhub/i18n/ja.json +221 -0
  110. mixhub_cli-0.2.0/mixhub/i18n/zh.json +221 -0
  111. mixhub_cli-0.2.0/mixhub/ops/__init__.py +8 -0
  112. {mixhub_cli-0.1.0 → mixhub_cli-0.2.0/mixhub/ops}/cost_report.py +18 -12
  113. mixhub_cli-0.2.0/mixhub/ops/doctor.py +27 -0
  114. {mixhub_cli-0.1.0 → mixhub_cli-0.2.0/mixhub/ops}/icons_font.py +12 -5
  115. mixhub_cli-0.2.0/mixhub/probe/__init__.py +21 -0
  116. mixhub_cli-0.2.0/mixhub/probe/away.py +218 -0
  117. mixhub_cli-0.2.0/mixhub/probe/capture.py +293 -0
  118. mixhub_cli-0.2.0/mixhub/probe/classify.py +551 -0
  119. mixhub_cli-0.2.0/mixhub/probe/menus.py +399 -0
  120. mixhub_cli-0.2.0/mixhub/probe/patterns.py +142 -0
  121. mixhub_cli-0.2.0/mixhub/probe/rules.py +156 -0
  122. mixhub_cli-0.2.0/mixhub/probe/text.py +59 -0
  123. mixhub_cli-0.2.0/mixhub/proc/__init__.py +26 -0
  124. mixhub_cli-0.2.0/mixhub/proc/classify.py +209 -0
  125. mixhub_cli-0.2.0/mixhub/proc/guard.py +136 -0
  126. mixhub_cli-0.2.0/mixhub/proc/identity.py +197 -0
  127. mixhub_cli-0.2.0/mixhub/proc/tree.py +211 -0
  128. mixhub_cli-0.2.0/mixhub/providers/__init__.py +212 -0
  129. mixhub_cli-0.2.0/mixhub/providers/adapters.py +139 -0
  130. mixhub_cli-0.2.0/mixhub/providers/claude.py +158 -0
  131. mixhub_cli-0.2.0/mixhub/providers/deepseek.py +105 -0
  132. mixhub_cli-0.2.0/mixhub/providers/grok.py +283 -0
  133. mixhub_cli-0.2.0/mixhub/providers/jsonl.py +48 -0
  134. mixhub_cli-0.2.0/mixhub/providers/launch.py +193 -0
  135. mixhub_cli-0.2.0/mixhub/providers/native_titles.py +499 -0
  136. mixhub_cli-0.2.0/mixhub/providers/preferences.py +37 -0
  137. mixhub_cli-0.2.0/mixhub/providers/presets.py +241 -0
  138. mixhub_cli-0.2.0/mixhub/providers/quota.py +429 -0
  139. mixhub_cli-0.2.0/mixhub/providers/readiness.py +346 -0
  140. mixhub_cli-0.2.0/mixhub/providers/resume.py +178 -0
  141. mixhub_cli-0.2.0/mixhub/providers/statusline.py +130 -0
  142. mixhub_cli-0.2.0/mixhub/providers/title_overrides.py +326 -0
  143. mixhub_cli-0.2.0/mixhub/quirks/__init__.py +118 -0
  144. mixhub_cli-0.2.0/mixhub/rules/__init__.py +14 -0
  145. mixhub_cli-0.2.0/mixhub/rules/blocks.py +108 -0
  146. mixhub_cli-0.2.0/mixhub/rules/sync.py +357 -0
  147. mixhub_cli-0.2.0/mixhub/rules/titles.py +28 -0
  148. mixhub_cli-0.2.0/mixhub/rules/trust.py +407 -0
  149. mixhub_cli-0.2.0/mixhub/surfaces/__init__.py +71 -0
  150. mixhub_cli-0.2.0/mixhub/tmux/__init__.py +32 -0
  151. mixhub_cli-0.2.0/mixhub/tmux/commands.py +87 -0
  152. mixhub_cli-0.2.0/mixhub/tmux/gateway.py +432 -0
  153. mixhub_cli-0.2.0/mixhub/tmux/headers.py +144 -0
  154. mixhub_cli-0.2.0/mixhub/tmux/home_shell.py +62 -0
  155. mixhub_cli-0.2.0/mixhub/tmux/new_sessions.py +232 -0
  156. mixhub_cli-0.2.0/mixhub/tmux/onexit.py +142 -0
  157. mixhub_cli-0.2.0/mixhub/tmux/outer.py +429 -0
  158. mixhub_cli-0.2.0/mixhub/tmux/pool.py +416 -0
  159. mixhub_cli-0.2.0/mixhub/tmux/session_state.py +162 -0
  160. mixhub_cli-0.2.0/mixhub/tmux/workspace.py +564 -0
  161. mixhub_cli-0.2.0/mixhub/tui/__init__.py +97 -0
  162. mixhub_cli-0.2.0/mixhub/tui/__main__.py +13 -0
  163. mixhub_cli-0.2.0/mixhub/tui/app.py +351 -0
  164. mixhub_cli-0.2.0/mixhub/tui/badges.py +175 -0
  165. mixhub_cli-0.2.0/mixhub/tui/caffeinate.py +79 -0
  166. mixhub_cli-0.2.0/mixhub/tui/core/__init__.py +56 -0
  167. mixhub_cli-0.2.0/mixhub/tui/core/canvas.py +214 -0
  168. mixhub_cli-0.2.0/mixhub/tui/core/events.py +70 -0
  169. mixhub_cli-0.2.0/mixhub/tui/core/geometry.py +61 -0
  170. mixhub_cli-0.2.0/mixhub/tui/core/hitmap.py +52 -0
  171. mixhub_cli-0.2.0/mixhub/tui/core/input.py +285 -0
  172. mixhub_cli-0.2.0/mixhub/tui/core/loop.py +77 -0
  173. mixhub_cli-0.2.0/mixhub/tui/core/style.py +483 -0
  174. mixhub_cli-0.2.0/mixhub/tui/core/widget.py +50 -0
  175. mixhub_cli-0.2.0/mixhub/tui/diag.py +35 -0
  176. mixhub_cli-0.2.0/mixhub/tui/drafts.py +126 -0
  177. mixhub_cli-0.2.0/mixhub/tui/hubtui/__init__.py +8 -0
  178. mixhub_cli-0.2.0/mixhub/tui/hubtui/actions.py +453 -0
  179. mixhub_cli-0.2.0/mixhub/tui/hubtui/attention.py +405 -0
  180. mixhub_cli-0.2.0/mixhub/tui/hubtui/colors.py +219 -0
  181. mixhub_cli-0.2.0/mixhub/tui/hubtui/draw.py +720 -0
  182. mixhub_cli-0.2.0/mixhub/tui/hubtui/footer.py +181 -0
  183. mixhub_cli-0.2.0/mixhub/tui/hubtui/header.py +145 -0
  184. mixhub_cli-0.2.0/mixhub/tui/hubtui/home.py +250 -0
  185. mixhub_cli-0.2.0/mixhub/tui/hubtui/layout.py +310 -0
  186. mixhub_cli-0.2.0/mixhub/tui/hubtui/loop.py +988 -0
  187. mixhub_cli-0.2.0/mixhub/tui/hubtui/mouse.py +682 -0
  188. mixhub_cli-0.2.0/mixhub/tui/hubtui/navigation.py +149 -0
  189. mixhub_cli-0.2.0/mixhub/tui/hubtui/popovers.py +242 -0
  190. mixhub_cli-0.2.0/mixhub/tui/hubtui/prompt.py +199 -0
  191. mixhub_cli-0.2.0/mixhub/tui/hubtui/reader.py +467 -0
  192. mixhub_cli-0.2.0/mixhub/tui/hubtui/refresh.py +374 -0
  193. mixhub_cli-0.2.0/mixhub/tui/hubtui/relay.py +410 -0
  194. mixhub_cli-0.2.0/mixhub/tui/hubtui/selection_sync.py +320 -0
  195. mixhub_cli-0.2.0/mixhub/tui/hubtui/sidebar.py +202 -0
  196. mixhub_cli-0.2.0/mixhub/tui/hubtui/wordmark_row.py +125 -0
  197. mixhub_cli-0.2.0/mixhub/tui/onboarding.py +118 -0
  198. mixhub_cli-0.2.0/mixhub/tui/paths.py +60 -0
  199. mixhub_cli-0.2.0/mixhub/tui/quota.py +633 -0
  200. mixhub_cli-0.2.0/mixhub/tui/selection.py +62 -0
  201. mixhub_cli-0.2.0/mixhub/tui/settings.py +34 -0
  202. mixhub_cli-0.2.0/mixhub/tui/sidebar_rows.py +181 -0
  203. mixhub_cli-0.2.0/mixhub/tui/web.py +75 -0
  204. mixhub_cli-0.2.0/mixhub/tui/widgets/__init__.py +67 -0
  205. mixhub_cli-0.2.0/mixhub/tui/widgets/chip.py +58 -0
  206. mixhub_cli-0.2.0/mixhub/tui/widgets/divider.py +31 -0
  207. mixhub_cli-0.2.0/mixhub/tui/widgets/header_bar.py +76 -0
  208. mixhub_cli-0.2.0/mixhub/tui/widgets/popover.py +434 -0
  209. mixhub_cli-0.2.0/mixhub/tui/widgets/preview_bar.py +79 -0
  210. mixhub_cli-0.2.0/mixhub/tui/widgets/quota_table.py +165 -0
  211. mixhub_cli-0.2.0/mixhub/tui/widgets/sidebar_row.py +125 -0
  212. mixhub_cli-0.2.0/mixhub/tui/widgets/toast.py +21 -0
  213. mixhub_cli-0.2.0/mixhub/tui/widgets/welcome.py +7 -0
  214. mixhub_cli-0.2.0/mixhub/tui/widgets/wordmark.py +348 -0
  215. mixhub_cli-0.2.0/mixhub/web/__init__.py +12 -0
  216. mixhub_cli-0.2.0/mixhub/web/__main__.py +10 -0
  217. mixhub_cli-0.2.0/mixhub/web/handler.py +179 -0
  218. mixhub_cli-0.2.0/mixhub/web/server.py +44 -0
  219. mixhub_cli-0.2.0/mixhub/web/static/app.js +1139 -0
  220. mixhub_cli-0.2.0/mixhub/web/static/index.html +192 -0
  221. mixhub_cli-0.2.0/mixhub/web/static/style.css +1036 -0
  222. mixhub_cli-0.2.0/pyproject.toml +49 -0
  223. mixhub_cli-0.2.0/scripts/demo_seed.py +217 -0
  224. {mixhub_cli-0.1.0 → mixhub_cli-0.2.0}/tests/browser_check.mjs +2 -2
  225. mixhub_cli-0.2.0/tests/compat/README.md +6 -0
  226. mixhub_cli-0.2.0/tests/compat/agy_claude_filter.py +109 -0
  227. mixhub_cli-0.2.0/tests/compat/channels.py +129 -0
  228. mixhub_cli-0.2.0/tests/compat/cost_report.py +26 -0
  229. mixhub_cli-0.2.0/tests/compat/handoff.py +126 -0
  230. mixhub_cli-0.2.0/tests/compat/icons_font.py +24 -0
  231. mixhub_cli-0.2.0/tests/compat/mixhub_cli.py +17 -0
  232. mixhub_cli-0.2.0/tests/compat/probe.py +101 -0
  233. mixhub_cli-0.2.0/tests/compat/proc_guard.py +58 -0
  234. mixhub_cli-0.2.0/tests/compat/protocols.py +59 -0
  235. mixhub_cli-0.2.0/tests/compat/providers.py +242 -0
  236. mixhub_cli-0.2.0/tests/compat/rules_sync.py +60 -0
  237. mixhub_cli-0.2.0/tests/compat/server.py +161 -0
  238. mixhub_cli-0.2.0/tests/compat/slack_bridge.py +62 -0
  239. mixhub_cli-0.2.0/tests/compat/tui.py +202 -0
  240. mixhub_cli-0.2.0/tests/compat/wordmark.py +33 -0
  241. mixhub_cli-0.2.0/tests/conftest.py +64 -0
  242. mixhub_cli-0.2.0/tests/fixtures/agy_1_4_7_altscreen_typed_120x40.bin +0 -0
  243. mixhub_cli-0.2.0/tests/fixtures/agy_1_4_7_altscreen_wrap_paste_40x30.bin +0 -0
  244. mixhub_cli-0.2.0/tests/test_agy_filter_mouse.py +279 -0
  245. mixhub_cli-0.2.0/tests/test_agy_filter_package.py +195 -0
  246. mixhub_cli-0.2.0/tests/test_agy_headless_and_hibernate_state.py +202 -0
  247. mixhub_cli-0.2.0/tests/test_anti_context_rot_primitives.py +289 -0
  248. mixhub_cli-0.2.0/tests/test_away_receipt_zero_emoji.py +1054 -0
  249. mixhub_cli-0.2.0/tests/test_bg_proc_scan.py +52 -0
  250. mixhub_cli-0.2.0/tests/test_caffeinate.py +20 -0
  251. mixhub_cli-0.2.0/tests/test_demo_seed.py +49 -0
  252. mixhub_cli-0.2.0/tests/test_e2e_ux_audit.py +3837 -0
  253. mixhub_cli-0.2.0/tests/test_env_names.py +46 -0
  254. mixhub_cli-0.2.0/tests/test_handoff_hold_and_fork.py +676 -0
  255. mixhub_cli-0.2.0/tests/test_harness_presets.py +500 -0
  256. mixhub_cli-0.2.0/tests/test_hub.py +3612 -0
  257. mixhub_cli-0.2.0/tests/test_hub_providers_layout.py +140 -0
  258. mixhub_cli-0.2.0/tests/test_i18n.py +205 -0
  259. mixhub_cli-0.2.0/tests/test_launch_forms.py +312 -0
  260. mixhub_cli-0.2.0/tests/test_modular_and_trust.py +452 -0
  261. mixhub_cli-0.2.0/tests/test_modular_channels_handoff.py +245 -0
  262. mixhub_cli-0.2.0/tests/test_opencode.py +189 -0
  263. mixhub_cli-0.2.0/tests/test_oss_desensitize_and_cc_ds.py +841 -0
  264. mixhub_cli-0.2.0/tests/test_package_import_rule.py +212 -0
  265. mixhub_cli-0.2.0/tests/test_packaging.py +50 -0
  266. mixhub_cli-0.2.0/tests/test_platform_support.py +159 -0
  267. mixhub_cli-0.2.0/tests/test_probe.py +658 -0
  268. mixhub_cli-0.2.0/tests/test_proc_guard.py +245 -0
  269. mixhub_cli-0.2.0/tests/test_providers_live.py +185 -0
  270. mixhub_cli-0.2.0/tests/test_rules_sync.py +175 -0
  271. mixhub_cli-0.2.0/tests/test_schema_resilience.py +268 -0
  272. mixhub_cli-0.2.0/tests/test_server.py +362 -0
  273. mixhub_cli-0.2.0/tests/test_slack_bridge.py +940 -0
  274. mixhub_cli-0.2.0/tests/test_state_root.py +160 -0
  275. mixhub_cli-0.2.0/tests/test_statusline_command.py +54 -0
  276. mixhub_cli-0.2.0/tests/test_subagent_filter.py +156 -0
  277. mixhub_cli-0.2.0/tests/test_telegram_bot_api_send.py +103 -0
  278. mixhub_cli-0.2.0/tests/test_test_isolation.py +42 -0
  279. mixhub_cli-0.2.0/tests/test_toml_fallback.py +34 -0
  280. mixhub_cli-0.2.0/tests/test_tui_core_primitives.py +357 -0
  281. mixhub_cli-0.2.0/tests/test_tui_module_layout.py +46 -0
  282. mixhub_cli-0.2.0/tests/test_update.py +182 -0
  283. mixhub_cli-0.2.0/tests/test_web_navigation.py +36 -0
  284. {mixhub_cli-0.1.0 → mixhub_cli-0.2.0}/tests/test_windows_compat.py +22 -7
  285. mixhub_cli-0.2.0/tests/test_zoom_toggle_and_badge.py +185 -0
  286. mixhub_cli-0.2.0/tests/webui_composer.test.mjs +610 -0
  287. mixhub_cli-0.1.0/.gitignore +0 -18
  288. mixhub_cli-0.1.0/PKG-INFO +0 -98
  289. mixhub_cli-0.1.0/README.md +0 -73
  290. mixhub_cli-0.1.0/bin/hub +0 -265
  291. mixhub_cli-0.1.0/bin/mixhub.js +0 -15
  292. mixhub_cli-0.1.0/channels.py +0 -2801
  293. mixhub_cli-0.1.0/handoff.py +0 -1294
  294. mixhub_cli-0.1.0/mixhub_cli.py +0 -25
  295. mixhub_cli-0.1.0/package.json +0 -46
  296. mixhub_cli-0.1.0/protocols.py +0 -643
  297. mixhub_cli-0.1.0/pyproject.toml +0 -60
  298. mixhub_cli-0.1.0/rules_sync.py +0 -291
  299. mixhub_cli-0.1.0/server.py +0 -2045
  300. mixhub_cli-0.1.0/slim_storage.py +0 -142
  301. mixhub_cli-0.1.0/static/app.js +0 -466
  302. mixhub_cli-0.1.0/static/index.html +0 -58
  303. mixhub_cli-0.1.0/static/style.css +0 -1
  304. mixhub_cli-0.1.0/tests/test_hub.py +0 -1400
  305. mixhub_cli-0.1.0/tui.py +0 -2927
  306. {mixhub_cli-0.1.0 → mixhub_cli-0.2.0}/bin/mixhub +0 -0
  307. {mixhub_cli-0.1.0 → mixhub_cli-0.2.0}/tests/mcp_client.mjs +0 -0
  308. {mixhub_cli-0.1.0 → mixhub_cli-0.2.0}/tests/test_handoff_ab.py +0 -0
@@ -0,0 +1,60 @@
1
+ # Runtime & user session state (never committed)
2
+ Preferences
3
+ active_working_set.json
4
+ unsent_drafts.json
5
+ session_names.json
6
+ data/
7
+ screenshots/
8
+ *.log
9
+ *.bak
10
+ .hub.pid
11
+ .hub.lock
12
+ hub_state.json
13
+ ACCEPTANCE.md
14
+ /index.html
15
+ bench_out.json
16
+
17
+ # Local preferences & backups (keep Preferences.example tracked)
18
+ /Preferences
19
+ /Preferences.*
20
+ !/Preferences.example
21
+
22
+ # All PID & lock files (covers both .hub.pid and hub.pid)
23
+ *.pid
24
+ *.lock
25
+
26
+ # Internal dated design notes & local scratch docs
27
+ docs/
28
+ /docs/internal/
29
+ /docs/*_20[0-9][0-9]-[0-9][0-9]-[0-9][0-9]*.md
30
+ *.local.*
31
+ *.orig
32
+
33
+ # Python & OS caches
34
+ __pycache__/
35
+ *.py[cod]
36
+ *$py.class
37
+ .pytest_cache/
38
+ dist/
39
+ build/
40
+ *.egg-info/
41
+ .DS_Store
42
+
43
+ # Kept on the maintainer's machine only (not part of the public tree)
44
+ MIXHUB_CHRONICLE.md
45
+ voice_transcribe.py
46
+ tests/test_voice_transcribe.py
47
+ slim_storage.py
48
+ mixhub/ops/slim_storage.py
49
+
50
+ # Agent worktrees
51
+ .claude/
52
+
53
+ # maintainer-local, not part of the release
54
+ package.json
55
+ bin/mixhub.js
56
+ scripts/git_steward.py
57
+ tests/test_git_steward.py
58
+ # local launchers for processes started before the root scripts moved
59
+ /tui.py
60
+ /slack_bridge.py
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ShinoKana
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,270 @@
1
+ Metadata-Version: 2.5
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.
5
+ Project-URL: Homepage, https://github.com/ShinoKana/mixhub
6
+ Project-URL: Repository, https://github.com/ShinoKana/mixhub.git
7
+ Project-URL: Issues, https://github.com/ShinoKana/mixhub/issues
8
+ Author: MixHub Contributors
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-agents,claude-code,codex,gemini,handoff,opencode,terminal,tmux
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Environment :: Web Environment
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: MacOS
18
+ Classifier: Operating System :: POSIX :: Linux
19
+ Classifier: Programming Language :: Python :: 3
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.10
22
+ Classifier: Programming Language :: Python :: 3.11
23
+ Classifier: Programming Language :: Python :: 3.12
24
+ Classifier: Programming Language :: Python :: 3.13
25
+ Requires-Python: >=3.10
26
+ Description-Content-Type: text/markdown
27
+
28
+ <div align="center">
29
+
30
+ # MixHub
31
+
32
+ **One switchboard for Claude Code, Gemini (AGY), Codex and OpenCode sessions**
33
+
34
+ [![License: MIT](https://img.shields.io/badge/license-MIT-596F62.svg)](#license)
35
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB.svg)](#install)
36
+ [![Dependencies: stdlib](https://img.shields.io/badge/python_dependencies-stdlib_only-596F62.svg)](#architecture)
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
+ </div>
41
+
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
+ └────────────────────────────────┴──────────────────────────────────────────────────────────────┘
60
+ ```
61
+
62
+ ## Why
63
+
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:
65
+
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.
69
+
70
+ ## Install
71
+
72
+ Requirements: Python 3.10+ and `tmux` (developed on tmux 3.6).
73
+
74
+ | Platform | Status |
75
+ | :--- | :--- |
76
+ | macOS | Supported |
77
+ | Linux, Windows via WSL | Experimental: the workspace, web workbench and CLI work; Terminal.app tab switching and the Dock badge are macOS-only |
78
+ | Native Windows | Not supported; run MixHub inside WSL |
79
+
80
+ ```bash
81
+ curl -fsSL https://raw.githubusercontent.com/ShinoKana/mixhub/main/install.sh | sh
82
+ ```
83
+
84
+ The script installs the `mixhub-cli` package (it provides the `mixhub` command) with uv, pipx or a private venv (whichever it finds first) and tells you if Python 3.10+ or tmux is missing. Installing it yourself works the same: `uv tool install mixhub-cli` or `pipx install mixhub-cli`.
85
+
86
+ ```bash
87
+ mixhub # open or reattach to the workspace
88
+ mixhub web # local web workbench at http://127.0.0.1:8765
89
+ mixhub doctor # check providers, channels, locks and terminal settings
90
+ ```
91
+
92
+ From a clone:
93
+
94
+ ```bash
95
+ git clone https://github.com/ShinoKana/mixhub.git
96
+ ln -sf "$PWD/mixhub/bin/mixhub" ~/.local/bin/mixhub
97
+ ```
98
+
99
+ ### Updating
100
+
101
+ ```bash
102
+ mixhub update # upgrade, then reload the sidebar, reader and web daemon on the new code
103
+ mixhub update --check # only say whether a newer version exists
104
+ ```
105
+
106
+ `mixhub update` uses whatever installed MixHub: `uv tool upgrade`, `pipx upgrade`, `pip install --upgrade` inside install.sh's venv, or `git pull --ff-only` in a clone. Running agent sessions are not interrupted. MixHub never updates itself: once a day it asks PyPI for the latest version in the background and, when there is a newer one, prints a one-line hint from `mixhub web`, `mixhub doctor` and the workspace's exit message. Set `"update_check": false` in Preferences or `MIXHUB_NO_UPDATE_CHECK=1` to turn the check off.
107
+
108
+ ## Supported agents
109
+
110
+ | Agent | Sessions read from | Resume |
111
+ | :--- | :--- | :--- |
112
+ | Claude Code | `~/.claude/projects/**.jsonl` | `claude --resume <id>` |
113
+ | Gemini via AGY | AGY conversation store | `agy --conversation <id>` |
114
+ | Codex | `~/.codex/sessions/` rollouts + state DB | `codex resume <id>` |
115
+ | OpenCode | `opencode.db` (read-only) | `opencode -s <id>` |
116
+
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.
118
+
119
+ ## The workspace
120
+
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.
122
+
123
+ **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
+
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).
126
+
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:
128
+
129
+ ```jsonc
130
+ // ~/.claude/settings.json
131
+ "statusLine": {"type": "command", "command": "mixhub statusline claude"}
132
+
133
+ // ~/.gemini/antigravity-cli/settings.json
134
+ "statusLine": {"type": "command", "command": "mixhub statusline agy", "enabled": true}
135
+ ```
136
+
137
+ Without it the footer estimates Claude's quota from transcripts.
138
+
139
+ **OpenCode tabs.** OpenCode keeps several sessions in one process. MixHub follows the tab you are on: the window is renamed to that session, and other tabs show a preview and open in their own window when resumed.
140
+
141
+ **Attention.** When a session finishes a turn or starts waiting while you are elsewhere, MixHub rings the outer terminal once, so Terminal.app shows a Dock badge and a counter in the window title.
142
+
143
+ **Crash recovery.** `mixhub shell-hook` prints a zsh snippet for `~/.zshrc`. If Terminal restores the window MixHub was in after a crash or forced quit, the snippet reattaches it.
144
+
145
+ ## Session lifecycle
146
+
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:
148
+
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 |
154
+
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.
156
+
157
+ ## Handoff between agents
158
+
159
+ When one agent runs out of quota or you want a second opinion, `mixhub handoff` writes a compact handoff bundle (under 2,000 tokens, ten sections: goal, acceptance criteria, decisions, rejected approaches, your verbatim constraints, git and file state, verification commands, open questions, next action, evidence pointers) plus a machine-readable `handoff.yaml`, and starts the target agent with it:
160
+
161
+ ```bash
162
+ mixhub handoff SESSION_ID --to codex --mode delegate
163
+ mixhub handoff SESSION_ID --to claude --mode review
164
+ mixhub handoff SESSION_ID --to agy --dry-run
165
+ ```
166
+
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.
168
+
169
+ ## Web workbench and notifications
170
+
171
+ <picture>
172
+ <source media="(prefers-color-scheme: dark)" srcset=".github/assets/web-dark.png">
173
+ <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
+ </picture>
175
+
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.
178
+
179
+ ## Language
180
+
181
+ The web workbench, terminal messages and handoff bundles are in English, Chinese or Japanese. Terminal text and handoff bundles use `MIXHUB_LANGUAGE`, else `"language"` in Preferences (`"auto"`, `"en"`, `"zh"`, `"ja"`), else your locale. The web workbench uses the language picked in its settings (`,`), else the Preferences language, else your browser's. Notifications follow the language you write to the agent in. Text missing in a language falls back to English.
182
+
183
+ Translations are one JSON file per language in [`mixhub/i18n/`](mixhub/i18n). To add a language, copy `en.json` to `<code>.json`, translate the values (keep the `{placeholders}`), and add the code to `LANGUAGES` in `mixhub/i18n/__init__.py`. Partial translations are fine.
184
+
185
+ ## Security
186
+
187
+ The web workbench and its API listen on `127.0.0.1` only and have no login. They can read every transcript MixHub sees and type into running agent sessions, so treat the port like a shell:
188
+
189
+ - 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
+ - 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
+ - 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.
193
+
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`).
195
+
196
+ ## Configuration
197
+
198
+ MixHub keeps its state (`data/`, `Preferences`, `hub_state.json`, the Web daemon's `.hub.pid` / `hub.log`) in one directory: `$MIXHUB_HOME` if set, otherwise the repository root when you run from a clone, otherwise `$XDG_DATA_HOME/mixhub` or `~/.local/share/mixhub`. Nothing is written next to an installed package.
199
+
200
+ Everything works without configuration. Optional settings live in a JSON file named `Preferences`, looked up at `$MIXHUB_PREFERENCES_PATH`, then `~/.config/mixhub/Preferences`, then the state directory:
201
+
202
+ ```json
203
+ {
204
+ "language": "auto",
205
+ "user_name": "Developer",
206
+ "claude_theme": false,
207
+ "trust_all_workspaces": false,
208
+ "trust_roots": ["~/workspace"],
209
+ "agy_mouse_shim": "auto",
210
+ "dispatch_scripts": ["my-agent-runner"],
211
+ "update_check": true
212
+ }
213
+ ```
214
+
215
+ [`Preferences.example`](Preferences.example) lists every option. Environment variables:
216
+
217
+ | Variable | Effect |
218
+ | :--- | :--- |
219
+ | `MIXHUB_HOME` | State directory |
220
+ | `MIXHUB_PREFERENCES_PATH` | Preferences file |
221
+ | `MIXHUB_PORT` | Web workbench port (default `8765`) |
222
+ | `MIXHUB_LANGUAGE` | Interface language, over Preferences and the locale |
223
+ | `MIXHUB_NO_OPEN=1` | `mixhub web` starts the daemon without opening a browser |
224
+ | `MIXHUB_NO_UPDATE_CHECK=1` | No daily update check |
225
+
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`).
227
+
228
+ ## Architecture
229
+
230
+ Python standard library only. The code lives in the `mixhub` package:
231
+
232
+ | Package | Responsibility |
233
+ | :--- | :--- |
234
+ | `mixhub/hub` | Session discovery per agent, transcript parsing, process binding, lifecycle and sleep guards, working set |
235
+ | `mixhub/providers` | Per-agent launch and resume commands, quota reading, native title sync, harness presets |
236
+ | `mixhub/tui` | The curses sidebar, preview reader and window header (`HubTUI`, composed from one mixin per concern) |
237
+ | `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 |
240
+ | `mixhub/probe` | Reading an agent's screen: working, waiting for approval, menus |
241
+ | `mixhub/proc` | Process identity and classification, safe-kill guards |
242
+ | `mixhub/rules` | Shared agent rules compiler and workspace trust |
243
+ | `mixhub/filters/agy_claude` | Optional PTY filter for AGY (theme, cursor, mouse) |
244
+ | `mixhub/web` | Loopback HTTP API and assets for the web workbench |
245
+ | `mixhub/cli` | The `mixhub` command |
246
+ | `mixhub/core` | Shared paths, launch commands, preferences, plugin protocols |
247
+
248
+ 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
+
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.
251
+
252
+ ## Roadmap
253
+
254
+ - 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
+ - Broader Linux polish.
257
+
258
+ ## Development
259
+
260
+ ```bash
261
+ python3 -m pytest -q
262
+ ```
263
+
264
+ `python3 scripts/demo_seed.py ~/.cache/mixhub-demo --web 8799` serves the web workbench on made-up sessions, which is how the screenshots are taken.
265
+
266
+ The tests never touch your real sessions or tmux servers: every state path is redirected to a temp directory and tmux tests use private sockets. `tests/test_oss_desensitize_and_cc_ds.py` checks every tracked file for personal paths and names.
267
+
268
+ ## License
269
+
270
+ MIT
@@ -0,0 +1,10 @@
1
+ {
2
+ "language": "auto",
3
+ "user_name": "Developer",
4
+ "claude_theme": false,
5
+ "trust_all_workspaces": false,
6
+ "trust_roots": ["~/workspace"],
7
+ "agy_mouse_shim": "auto",
8
+ "dispatch_scripts": ["my-agent-runner"],
9
+ "update_check": true
10
+ }
@@ -0,0 +1,243 @@
1
+ <div align="center">
2
+
3
+ # MixHub
4
+
5
+ **One switchboard for Claude Code, Gemini (AGY), Codex and OpenCode sessions**
6
+
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-596F62.svg)](#license)
8
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB.svg)](#install)
9
+ [![Dependencies: stdlib](https://img.shields.io/badge/python_dependencies-stdlib_only-596F62.svg)](#architecture)
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
+ </div>
14
+
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
+ └────────────────────────────────┴──────────────────────────────────────────────────────────────┘
33
+ ```
34
+
35
+ ## Why
36
+
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:
38
+
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.
42
+
43
+ ## Install
44
+
45
+ Requirements: Python 3.10+ and `tmux` (developed on tmux 3.6).
46
+
47
+ | Platform | Status |
48
+ | :--- | :--- |
49
+ | macOS | Supported |
50
+ | Linux, Windows via WSL | Experimental: the workspace, web workbench and CLI work; Terminal.app tab switching and the Dock badge are macOS-only |
51
+ | Native Windows | Not supported; run MixHub inside WSL |
52
+
53
+ ```bash
54
+ curl -fsSL https://raw.githubusercontent.com/ShinoKana/mixhub/main/install.sh | sh
55
+ ```
56
+
57
+ The script installs the `mixhub-cli` package (it provides the `mixhub` command) with uv, pipx or a private venv (whichever it finds first) and tells you if Python 3.10+ or tmux is missing. Installing it yourself works the same: `uv tool install mixhub-cli` or `pipx install mixhub-cli`.
58
+
59
+ ```bash
60
+ mixhub # open or reattach to the workspace
61
+ mixhub web # local web workbench at http://127.0.0.1:8765
62
+ mixhub doctor # check providers, channels, locks and terminal settings
63
+ ```
64
+
65
+ From a clone:
66
+
67
+ ```bash
68
+ git clone https://github.com/ShinoKana/mixhub.git
69
+ ln -sf "$PWD/mixhub/bin/mixhub" ~/.local/bin/mixhub
70
+ ```
71
+
72
+ ### Updating
73
+
74
+ ```bash
75
+ mixhub update # upgrade, then reload the sidebar, reader and web daemon on the new code
76
+ mixhub update --check # only say whether a newer version exists
77
+ ```
78
+
79
+ `mixhub update` uses whatever installed MixHub: `uv tool upgrade`, `pipx upgrade`, `pip install --upgrade` inside install.sh's venv, or `git pull --ff-only` in a clone. Running agent sessions are not interrupted. MixHub never updates itself: once a day it asks PyPI for the latest version in the background and, when there is a newer one, prints a one-line hint from `mixhub web`, `mixhub doctor` and the workspace's exit message. Set `"update_check": false` in Preferences or `MIXHUB_NO_UPDATE_CHECK=1` to turn the check off.
80
+
81
+ ## Supported agents
82
+
83
+ | Agent | Sessions read from | Resume |
84
+ | :--- | :--- | :--- |
85
+ | Claude Code | `~/.claude/projects/**.jsonl` | `claude --resume <id>` |
86
+ | Gemini via AGY | AGY conversation store | `agy --conversation <id>` |
87
+ | Codex | `~/.codex/sessions/` rollouts + state DB | `codex resume <id>` |
88
+ | OpenCode | `opencode.db` (read-only) | `opencode -s <id>` |
89
+
90
+ `✎ 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.
91
+
92
+ ## The workspace
93
+
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.
95
+
96
+ **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
+
98
+ **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
+
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:
101
+
102
+ ```jsonc
103
+ // ~/.claude/settings.json
104
+ "statusLine": {"type": "command", "command": "mixhub statusline claude"}
105
+
106
+ // ~/.gemini/antigravity-cli/settings.json
107
+ "statusLine": {"type": "command", "command": "mixhub statusline agy", "enabled": true}
108
+ ```
109
+
110
+ Without it the footer estimates Claude's quota from transcripts.
111
+
112
+ **OpenCode tabs.** OpenCode keeps several sessions in one process. MixHub follows the tab you are on: the window is renamed to that session, and other tabs show a preview and open in their own window when resumed.
113
+
114
+ **Attention.** When a session finishes a turn or starts waiting while you are elsewhere, MixHub rings the outer terminal once, so Terminal.app shows a Dock badge and a counter in the window title.
115
+
116
+ **Crash recovery.** `mixhub shell-hook` prints a zsh snippet for `~/.zshrc`. If Terminal restores the window MixHub was in after a crash or forced quit, the snippet reattaches it.
117
+
118
+ ## Session lifecycle
119
+
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:
121
+
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 |
127
+
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.
129
+
130
+ ## Handoff between agents
131
+
132
+ When one agent runs out of quota or you want a second opinion, `mixhub handoff` writes a compact handoff bundle (under 2,000 tokens, ten sections: goal, acceptance criteria, decisions, rejected approaches, your verbatim constraints, git and file state, verification commands, open questions, next action, evidence pointers) plus a machine-readable `handoff.yaml`, and starts the target agent with it:
133
+
134
+ ```bash
135
+ mixhub handoff SESSION_ID --to codex --mode delegate
136
+ mixhub handoff SESSION_ID --to claude --mode review
137
+ mixhub handoff SESSION_ID --to agy --dry-run
138
+ ```
139
+
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.
141
+
142
+ ## Web workbench and notifications
143
+
144
+ <picture>
145
+ <source media="(prefers-color-scheme: dark)" srcset=".github/assets/web-dark.png">
146
+ <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
+ </picture>
148
+
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.
151
+
152
+ ## Language
153
+
154
+ The web workbench, terminal messages and handoff bundles are in English, Chinese or Japanese. Terminal text and handoff bundles use `MIXHUB_LANGUAGE`, else `"language"` in Preferences (`"auto"`, `"en"`, `"zh"`, `"ja"`), else your locale. The web workbench uses the language picked in its settings (`,`), else the Preferences language, else your browser's. Notifications follow the language you write to the agent in. Text missing in a language falls back to English.
155
+
156
+ Translations are one JSON file per language in [`mixhub/i18n/`](mixhub/i18n). To add a language, copy `en.json` to `<code>.json`, translate the values (keep the `{placeholders}`), and add the code to `LANGUAGES` in `mixhub/i18n/__init__.py`. Partial translations are fine.
157
+
158
+ ## Security
159
+
160
+ The web workbench and its API listen on `127.0.0.1` only and have no login. They can read every transcript MixHub sees and type into running agent sessions, so treat the port like a shell:
161
+
162
+ - 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
+ - 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
+ - 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.
166
+
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`).
168
+
169
+ ## Configuration
170
+
171
+ MixHub keeps its state (`data/`, `Preferences`, `hub_state.json`, the Web daemon's `.hub.pid` / `hub.log`) in one directory: `$MIXHUB_HOME` if set, otherwise the repository root when you run from a clone, otherwise `$XDG_DATA_HOME/mixhub` or `~/.local/share/mixhub`. Nothing is written next to an installed package.
172
+
173
+ Everything works without configuration. Optional settings live in a JSON file named `Preferences`, looked up at `$MIXHUB_PREFERENCES_PATH`, then `~/.config/mixhub/Preferences`, then the state directory:
174
+
175
+ ```json
176
+ {
177
+ "language": "auto",
178
+ "user_name": "Developer",
179
+ "claude_theme": false,
180
+ "trust_all_workspaces": false,
181
+ "trust_roots": ["~/workspace"],
182
+ "agy_mouse_shim": "auto",
183
+ "dispatch_scripts": ["my-agent-runner"],
184
+ "update_check": true
185
+ }
186
+ ```
187
+
188
+ [`Preferences.example`](Preferences.example) lists every option. Environment variables:
189
+
190
+ | Variable | Effect |
191
+ | :--- | :--- |
192
+ | `MIXHUB_HOME` | State directory |
193
+ | `MIXHUB_PREFERENCES_PATH` | Preferences file |
194
+ | `MIXHUB_PORT` | Web workbench port (default `8765`) |
195
+ | `MIXHUB_LANGUAGE` | Interface language, over Preferences and the locale |
196
+ | `MIXHUB_NO_OPEN=1` | `mixhub web` starts the daemon without opening a browser |
197
+ | `MIXHUB_NO_UPDATE_CHECK=1` | No daily update check |
198
+
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`).
200
+
201
+ ## Architecture
202
+
203
+ Python standard library only. The code lives in the `mixhub` package:
204
+
205
+ | Package | Responsibility |
206
+ | :--- | :--- |
207
+ | `mixhub/hub` | Session discovery per agent, transcript parsing, process binding, lifecycle and sleep guards, working set |
208
+ | `mixhub/providers` | Per-agent launch and resume commands, quota reading, native title sync, harness presets |
209
+ | `mixhub/tui` | The curses sidebar, preview reader and window header (`HubTUI`, composed from one mixin per concern) |
210
+ | `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 |
213
+ | `mixhub/probe` | Reading an agent's screen: working, waiting for approval, menus |
214
+ | `mixhub/proc` | Process identity and classification, safe-kill guards |
215
+ | `mixhub/rules` | Shared agent rules compiler and workspace trust |
216
+ | `mixhub/filters/agy_claude` | Optional PTY filter for AGY (theme, cursor, mouse) |
217
+ | `mixhub/web` | Loopback HTTP API and assets for the web workbench |
218
+ | `mixhub/cli` | The `mixhub` command |
219
+ | `mixhub/core` | Shared paths, launch commands, preferences, plugin protocols |
220
+
221
+ 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
+
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.
224
+
225
+ ## Roadmap
226
+
227
+ - 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
+ - Broader Linux polish.
230
+
231
+ ## Development
232
+
233
+ ```bash
234
+ python3 -m pytest -q
235
+ ```
236
+
237
+ `python3 scripts/demo_seed.py ~/.cache/mixhub-demo --web 8799` serves the web workbench on made-up sessions, which is how the screenshots are taken.
238
+
239
+ The tests never touch your real sessions or tmux servers: every state path is redirected to a temp directory and tmux tests use private sockets. `tests/test_oss_desensitize_and_cc_ds.py` checks every tracked file for personal paths and names.
240
+
241
+ ## License
242
+
243
+ MIT
@@ -0,0 +1,21 @@
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).
3
+
4
+ Thin entry point: the dispatcher and every command live in `mixhub.cli` (`main`, `daemon`, `sessions`, `tools`, `workspace`).
5
+ """
6
+ import os
7
+ import subprocess
8
+ import sys
9
+
10
+ _ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
11
+ if _ROOT not in sys.path:
12
+ sys.path.insert(0, _ROOT)
13
+
14
+ from mixhub.cli.main import main # noqa: E402
15
+
16
+ if __name__ == "__main__":
17
+ try:
18
+ sys.exit(main())
19
+ except (OSError, RuntimeError, subprocess.SubprocessError) as exc:
20
+ print(str(exc), file=sys.stderr)
21
+ sys.exit(1)