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