mixhub-cli 0.2.0__tar.gz → 0.2.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mixhub_cli-0.2.2/.github/assets/terminal.png +0 -0
- mixhub_cli-0.2.2/.github/assets/web-dark.png +0 -0
- mixhub_cli-0.2.2/.github/assets/web-light.png +0 -0
- mixhub_cli-0.2.2/.github/workflows/publish.yml +28 -0
- mixhub_cli-0.2.2/.github/workflows/test.yml +34 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/.gitignore +3 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/PKG-INFO +96 -53
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/Preferences.example +3 -3
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/README.md +94 -51
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/bin/hub +1 -1
- mixhub_cli-0.2.2/mixhub/__init__.py +12 -0
- mixhub_cli-0.2.2/mixhub/channels/__init__.py +55 -0
- mixhub_cli-0.2.2/mixhub/channels/base.py +35 -0
- mixhub_cli-0.2.2/mixhub/channels/cli.py +92 -0
- mixhub_cli-0.2.2/mixhub/channels/config.py +165 -0
- mixhub_cli-0.2.2/mixhub/channels/hooks.py +92 -0
- mixhub_cli-0.2.2/mixhub/channels/manager.py +73 -0
- mixhub_cli-0.2.2/mixhub/channels/slack.py +57 -0
- mixhub_cli-0.2.2/mixhub/channels/telegram.py +263 -0
- mixhub_cli-0.2.2/mixhub/channels/text.py +38 -0
- mixhub_cli-0.2.2/mixhub/channels/webhook.py +50 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/__init__.py +2 -2
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/main.py +13 -42
- mixhub_cli-0.2.2/mixhub/cli/sessions.py +35 -0
- mixhub_cli-0.2.2/mixhub/cli/tools.py +19 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/update.py +2 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/workspace.py +53 -17
- mixhub_cli-0.2.2/mixhub/core/__init__.py +78 -0
- mixhub_cli-0.2.2/mixhub/core/browser.py +40 -0
- mixhub_cli-0.2.2/mixhub/core/cli_version.py +75 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/launch.py +9 -11
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/paths.py +0 -14
- mixhub_cli-0.2.2/mixhub/core/prefs.py +61 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/state.py +0 -24
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/textwidth.py +14 -10
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/__init__.py +2 -2
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/main.py +6 -13
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/prefs.py +2 -33
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/stream.py +5 -38
- mixhub_cli-0.2.2/mixhub/handoff/__init__.py +60 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/bundle.py +1 -53
- mixhub_cli-0.2.2/mixhub/handoff/disposition.py +23 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/extract.py +0 -13
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/relay.py +19 -85
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/__init__.py +4 -15
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/actions.py +23 -2
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/discovery.py +140 -18
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/hub.py +2 -0
- mixhub_cli-0.2.2/mixhub/hub/integrations.py +100 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/lifecycle.py +27 -176
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/pane_probe.py +10 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/processes.py +35 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/schema.py +4 -0
- mixhub_cli-0.2.2/mixhub/hub/send.py +142 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/session_filters.py +63 -44
- mixhub_cli-0.2.2/mixhub/hub/shell.py +85 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/sleep_blockers.py +5 -5
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/summary.py +11 -197
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/util.py +3 -2
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/working_set.py +0 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/i18n/__init__.py +5 -5
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/i18n/en.json +41 -34
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/i18n/ja.json +42 -35
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/i18n/zh.json +50 -43
- mixhub_cli-0.2.2/mixhub/ops/__init__.py +8 -0
- mixhub_cli-0.2.2/mixhub/ops/doctor.py +156 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/__init__.py +2 -4
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/capture.py +0 -11
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/classify.py +2 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/patterns.py +4 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/classify.py +2 -3
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/identity.py +13 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/__init__.py +1 -63
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/claude.py +1 -3
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/jsonl.py +1 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/launch.py +0 -30
- mixhub_cli-0.2.2/mixhub/providers/preferences.py +13 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/presets.py +1 -37
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/quota.py +8 -3
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/readiness.py +10 -107
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/statusline.py +16 -8
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/title_overrides.py +16 -73
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/rules/__init__.py +0 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/rules/sync.py +1 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/rules/trust.py +5 -38
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/__init__.py +1 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/gateway.py +17 -51
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/headers.py +1 -1
- mixhub_cli-0.2.2/mixhub/tmux/home_shell.py +141 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/new_sessions.py +122 -11
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/onexit.py +48 -38
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/outer.py +20 -18
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/pool.py +29 -9
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/session_state.py +22 -10
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/workspace.py +40 -74
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/__init__.py +1 -4
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/__main__.py +1 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/app.py +20 -54
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/badges.py +3 -49
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/style.py +6 -22
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/drafts.py +35 -2
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/__init__.py +2 -2
- mixhub_cli-0.2.2/mixhub/tui/hubtui/actions.py +261 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/attention.py +15 -13
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/colors.py +6 -6
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/draw.py +7 -10
- mixhub_cli-0.2.2/mixhub/tui/hubtui/evq.py +99 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/home.py +43 -23
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/layout.py +8 -21
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/loop.py +31 -190
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/mouse.py +0 -28
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/popovers.py +4 -19
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/reader.py +11 -36
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/relay.py +3 -63
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/selection_sync.py +32 -4
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/sidebar.py +28 -36
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/wordmark_row.py +1 -1
- mixhub_cli-0.2.2/mixhub/tui/settings.py +9 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/web.py +2 -17
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/__init__.py +0 -6
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/popover.py +2 -51
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/quota_table.py +1 -52
- mixhub_cli-0.2.2/mixhub/tui/widgets/toast.py +17 -0
- mixhub_cli-0.2.2/mixhub/tui/widgets/wordmark.py +203 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/handler.py +1 -18
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/static/app.js +60 -49
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/static/index.html +25 -26
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/static/style.css +9 -5
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/pyproject.toml +1 -1
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/scripts/demo_seed.py +95 -7
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/agy_claude_filter.py +3 -11
- mixhub_cli-0.2.2/tests/compat/channels.py +28 -0
- mixhub_cli-0.2.2/tests/compat/handoff.py +56 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/probe.py +1 -9
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/providers.py +0 -67
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/rules_sync.py +2 -10
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/server.py +1 -2
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/tui.py +1 -4
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/wordmark.py +7 -8
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_agy_filter_package.py +2 -3
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_agy_headless_and_hibernate_state.py +18 -26
- mixhub_cli-0.2.2/tests/test_agy_headless_continued.py +157 -0
- mixhub_cli-0.2.2/tests/test_auto_sleep_pref.py +86 -0
- mixhub_cli-0.2.2/tests/test_channels_config.py +108 -0
- mixhub_cli-0.2.2/tests/test_consumer_checks.py +31 -0
- mixhub_cli-0.2.2/tests/test_demo_seed.py +95 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_e2e_ux_audit.py +522 -411
- mixhub_cli-0.2.2/tests/test_first_run.py +52 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_handoff_hold_and_fork.py +0 -331
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_harness_presets.py +8 -203
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_hub.py +292 -431
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_hub_providers_layout.py +2 -2
- mixhub_cli-0.2.2/tests/test_hubpool_duplicate_windows.py +74 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_i18n.py +9 -40
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_launch_forms.py +13 -22
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_modular_and_trust.py +9 -36
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_modular_channels_handoff.py +8 -108
- mixhub_cli-0.2.2/tests/test_no_agent_file_deletion.py +79 -0
- mixhub_cli-0.2.2/tests/test_notify_hooks.py +117 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_oss_desensitize_and_cc_ds.py +32 -320
- mixhub_cli-0.2.2/tests/test_own_tmux_server.py +126 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_package_import_rule.py +5 -11
- mixhub_cli-0.2.2/tests/test_platform_support.py +307 -0
- mixhub_cli-0.2.2/tests/test_preferences_lookup.py +67 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_probe.py +27 -125
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_server.py +44 -0
- mixhub_cli-0.2.2/tests/test_session_hygiene.py +316 -0
- mixhub_cli-0.2.2/tests/test_sidebar_pinned_order.py +22 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_state_root.py +17 -21
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_telegram_bot_api_send.py +2 -4
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_tui_core_primitives.py +12 -10
- mixhub_cli-0.2.2/tests/test_windows_compat.py +103 -0
- mixhub_cli-0.2.2/tests/test_wordmark.py +87 -0
- mixhub_cli-0.2.2/tests/test_working_set_removal_log.py +32 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_zoom_toggle_and_badge.py +22 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/webui_composer.test.mjs +162 -21
- mixhub_cli-0.2.0/.github/assets/web-dark.png +0 -0
- mixhub_cli-0.2.0/.github/assets/web-light.png +0 -0
- 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/hub/shell.py +0 -49
- 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/tmux/home_shell.py +0 -62
- mixhub_cli-0.2.0/mixhub/tui/hubtui/actions.py +0 -453
- 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.2}/LICENSE +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/bin/mixhub +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/install.sh +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/__main__.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/cli/daemon.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/core/env.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/__init__.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/__main__.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/cursor.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/debuglog.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/mouse.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/patterns.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/filters/agy_claude/vscreen.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/contract.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/resume.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/handoff/text.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/constants.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/index.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/listing.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/metrics.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/opencode.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/text.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/titles.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/hub/transcript.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/menus.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/rules.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/probe/text.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/__init__.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/guard.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/proc/tree.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/deepseek.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/native_titles.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/providers/resume.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/rules/blocks.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tmux/commands.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/caffeinate.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/__init__.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/canvas.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/events.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/geometry.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/hitmap.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/input.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/loop.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/core/widget.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/diag.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/footer.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/header.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/navigation.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/prompt.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/hubtui/refresh.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/paths.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/quota.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/selection.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/sidebar_rows.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/chip.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/divider.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/header_bar.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/preview_bar.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/tui/widgets/sidebar_row.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/__init__.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/__main__.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/mixhub/web/server.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/browser_check.mjs +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/README.md +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/mixhub_cli.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/compat/proc_guard.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/conftest.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/fixtures/agy_1_4_7_altscreen_typed_120x40.bin +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/fixtures/agy_1_4_7_altscreen_wrap_paste_40x30.bin +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/mcp_client.mjs +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_agy_filter_mouse.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_bg_proc_scan.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_caffeinate.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_env_names.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_opencode.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_packaging.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_proc_guard.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_providers_live.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_rules_sync.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_schema_resilience.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_statusline_command.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_subagent_filter.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_test_isolation.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_tui_module_layout.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_update.py +0 -0
- {mixhub_cli-0.2.0 → mixhub_cli-0.2.2}/tests/test_web_navigation.py +0 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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.2
|
|
4
|
+
Summary: A terminal sidebar for your Claude Code, Codex, Gemini and OpenCode sessions.
|
|
5
5
|
Project-URL: Homepage, https://github.com/ShinoKana/mixhub
|
|
6
6
|
Project-URL: Repository, https://github.com/ShinoKana/mixhub.git
|
|
7
7
|
Project-URL: Issues, https://github.com/ShinoKana/mixhub/issues
|
|
@@ -29,43 +29,54 @@ Description-Content-Type: text/markdown
|
|
|
29
29
|
|
|
30
30
|
# MixHub
|
|
31
31
|
|
|
32
|
-
**
|
|
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
|
+
| | Other agents | Runs in | Status | Outside sessions |
|
|
68
|
+
| :--- | :--- | :--- | :--- | :--- |
|
|
69
|
+
| MixHub | Codex, Gemini, OpenCode | tmux | session files | listed |
|
|
70
|
+
| [Agent View](https://code.claude.com/docs/en/agent-view) | none | `claude` CLI | yes | after `/bg` |
|
|
71
|
+
| [cmux](https://github.com/manaflow-ai/cmux) | Codex, Gemini, OpenCode, … | macOS app | hooks / OSC | – |
|
|
72
|
+
| [claude-squad](https://github.com/smtg-ai/claude-squad) | Codex, Gemini, Aider | tmux | – | no |
|
|
73
|
+
| [ccmanager](https://github.com/kbwo/ccmanager) | Codex, Gemini, OpenCode, … | any terminal | yes | – |
|
|
74
|
+
| [agent-deck](https://github.com/asheshgoplani/agent-deck) | Codex, Gemini, OpenCode, … | tmux | yes | no |
|
|
75
|
+
| [tmux-agent-status](https://github.com/samleeney/tmux-agent-status) | Codex, Devin | your tmux | hooks | if in tmux |
|
|
76
|
+
|
|
77
|
+
– means the project's docs don't say.
|
|
78
|
+
|
|
79
|
+
If you only use Claude Code, Agent View is built in. If you want a git worktree per task, claude-squad and ccmanager make one for you (agent-deck optionally); MixHub doesn't touch git.
|
|
69
80
|
|
|
70
81
|
## Install
|
|
71
82
|
|
|
@@ -86,7 +97,7 @@ The script installs the `mixhub-cli` package (it provides the `mixhub` command)
|
|
|
86
97
|
```bash
|
|
87
98
|
mixhub # open or reattach to the workspace
|
|
88
99
|
mixhub web # local web workbench at http://127.0.0.1:8765
|
|
89
|
-
mixhub doctor # check
|
|
100
|
+
mixhub doctor # check agent CLIs, tmux, notification channels and terminal settings
|
|
90
101
|
```
|
|
91
102
|
|
|
92
103
|
From a clone:
|
|
@@ -114,17 +125,17 @@ mixhub update --check # only say whether a newer version exists
|
|
|
114
125
|
| Codex | `~/.codex/sessions/` rollouts + state DB | `codex resume <id>` |
|
|
115
126
|
| OpenCode | `opencode.db` (read-only) | `opencode -s <id>` |
|
|
116
127
|
|
|
117
|
-
`✎ New session` can also start Claude Code with another model behind it (DeepSeek, Kimi, Qwen and GLM presets); the badge shows which model a session uses.
|
|
128
|
+
`✎ New session` opens the new session in the working directory of the session selected in the sidebar, so to start in a project folder, click one of that folder's sessions first. With nothing selected, including the `LAUNCH` buttons on Home, it uses the folder you ran `mixhub` from. It can also start Claude Code with another model behind it (DeepSeek, Kimi, Qwen and GLM presets); the badge shows which model a session uses.
|
|
118
129
|
|
|
119
130
|
## The workspace
|
|
120
131
|
|
|
121
|
-
**Sidebar.** Sessions are grouped into `Pinned`, `Active` (sessions you are working with) and `Recent`. A row shows the agent's brand-colored badge, the title the agent itself gave the session, and its age. The badge animates while the agent is working
|
|
132
|
+
**Sidebar.** Sessions are grouped into `Pinned`, `Active` (sessions you are working with) and `Recent`. A row shows the agent's brand-colored badge, the title the agent itself gave the session, and its age. The badge animates while the agent is working; when the agent needs approval or an answer it stops, turns bold amber, and the age turns yellow. Unread replies are bold. `▾ mixhub` filters by agent, `‹ ›` walks back and forward through what you looked at, `◧` collapses the sidebar. Hover a row to pin it. The sidebar is entirely click-driven; keys go to the agent. Subagent runs, headless or scripted runs, and sessions whose working directory is under `/tmp` are not listed.
|
|
122
133
|
|
|
123
134
|
**Right pane.** Clicking a session that is running in MixHub switches to its live CLI window. Clicking any other session shows a read-only transcript preview with a turn minimap; `[ Resume ↵ ]` starts the CLI in place, and `[ Go to Tab ]` jumps to a session that is running in another Terminal tab. Each live window has a two-row header with the working directory, session id and title.
|
|
124
135
|
|
|
125
|
-
**Home.** With nothing selected, the right pane holds a plain login shell under the preview, for quick commands without leaving the workspace. Clear it the usual way (`clear` or Ctrl+L).
|
|
136
|
+
**Home.** With nothing selected, the right pane holds a plain login shell under the preview, for quick commands without leaving the workspace. Clear it the usual way (`clear` or Ctrl+L). An agent started there (`claude`, `codex`, `opencode`, `agy`) moves to its own window in `Active`, the same as `✎ New session`, and Home gets a fresh shell; one-shot runs like `claude -p` stay in the shell.
|
|
126
137
|
|
|
127
|
-
**Footer.**
|
|
138
|
+
**Footer.** Used `5h` and weekly quota with reset times for each agent that has at least one session on this machine, plus CPU and memory. Codex quota comes from its own session files. For exact Claude Code and Gemini (AGY) numbers, point their status line at MixHub, which records the quota each agent reports and prints a short status line back:
|
|
128
139
|
|
|
129
140
|
```jsonc
|
|
130
141
|
// ~/.claude/settings.json
|
|
@@ -144,15 +155,11 @@ Without it the footer estimates Claude's quota from transcripts.
|
|
|
144
155
|
|
|
145
156
|
## Session lifecycle
|
|
146
157
|
|
|
147
|
-
MixHub separates whether a session is listed as `Active` from whether its process is running.
|
|
158
|
+
MixHub separates whether a session is listed as `Active` from whether its process is running. Sessions keep running until you `/exit` them or close them from the sidebar. To have idle ones put to sleep (process stopped, session kept in `Active`, resumed in place when you click it), set `auto_sleep_hours` in `Preferences`; it is off by default.
|
|
148
159
|
|
|
149
|
-
|
|
150
|
-
| :--- | :--- |
|
|
151
|
-
| Normal | 12 hours |
|
|
152
|
-
| Moderate memory pressure | 6 hours |
|
|
153
|
-
| Critical memory pressure, or more than 6 live sessions | 4 hours |
|
|
160
|
+
A session is never put to sleep while it is working or waiting for approval, has running subagents or open background tasks, or has user processes running on its terminal. If the process table can't be read, nothing is put to sleep. Only sessions running in MixHub's own windows are ever put to sleep, automatically or from the web; an agent you started in another terminal is listed but never stopped. `/exit` in the agent moves the session to `Recent`; a system shutdown keeps `Active` as it was.
|
|
154
161
|
|
|
155
|
-
|
|
162
|
+
On macOS, while an agent is working and the Mac runs on power, MixHub keeps it from idle-sleeping (`caffeinate -i`, released when the work ends; never on battery).
|
|
156
163
|
|
|
157
164
|
## Handoff between agents
|
|
158
165
|
|
|
@@ -164,7 +171,7 @@ mixhub handoff SESSION_ID --to claude --mode review
|
|
|
164
171
|
mixhub handoff SESSION_ID --to agy --dry-run
|
|
165
172
|
```
|
|
166
173
|
|
|
167
|
-
The new session is titled `↳ <source title>`.
|
|
174
|
+
The new session is titled `↳ <source title>`.
|
|
168
175
|
|
|
169
176
|
## Web workbench and notifications
|
|
170
177
|
|
|
@@ -173,8 +180,45 @@ The new session is titled `↳ <source title>`. A write lease per working tree r
|
|
|
173
180
|
<img alt="The MixHub web workbench: a session list with quota on the left and a Claude Code transcript on the right" src=".github/assets/web-light.png">
|
|
174
181
|
</picture>
|
|
175
182
|
|
|
176
|
-
- `mixhub web` serves a loopback-only web UI: search across all sessions
|
|
177
|
-
- `mixhub notify`
|
|
183
|
+
- `mixhub web` serves a loopback-only web UI: search across all sessions and full transcripts. The terminal workspace is where MixHub gets used day to day; the web workbench sees much less use and has rougher edges, so please [open an issue](../../issues) when something there looks wrong.
|
|
184
|
+
- `mixhub notify` sends a one-way message to Telegram, a Slack incoming webhook or any URL that takes a JSON POST. It only tells you something happened; replying and approving stay in the agents' own remote control.
|
|
185
|
+
|
|
186
|
+
### Notification channels
|
|
187
|
+
|
|
188
|
+
Nothing is sent until you set up a channel in `~/.config/mixhub/channels.json`. Every section is optional:
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"telegram": {
|
|
193
|
+
"bot_token": "123456:ABC...",
|
|
194
|
+
"chat_id": -1001234567890,
|
|
195
|
+
"topics": {"builds": 42, "reviews": 57},
|
|
196
|
+
"default_topic": "builds",
|
|
197
|
+
"routing_rules": {"reviews": ["review", "pr"]}
|
|
198
|
+
},
|
|
199
|
+
"slack": {"webhook_url": "https://hooks.slack.com/services/..."},
|
|
200
|
+
"webhook": {"url": "https://example.com/mixhub", "headers": {"Authorization": "Bearer ..."}}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
- Telegram goes through the Bot API (`sendMessage`, plus `sendDocument` for an attached file). `topics` maps forum topic names to thread ids. A message goes to the topic you name with `--topic`, else the first topic whose `routing_rules` keyword appears in the session's folder, title or the message, else `default_topic`, else the chat itself. To send through your own command instead, set `"tgnotify_bin"`. It is called as `<bin> -t <topic> [--alert] [--file F] <text>`.
|
|
205
|
+
- Slack posts to the channel the incoming webhook belongs to.
|
|
206
|
+
- `webhook` receives `{"text", "title", "session_id", "tool", "topic"}`.
|
|
207
|
+
|
|
208
|
+
The notification settings in the web workbench (the bell) show which channels are set up and send a test message.
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
mixhub notify "Deploy finished" # every configured channel
|
|
212
|
+
mixhub notify --channel telegram --topic builds --alert "Tests failed"
|
|
213
|
+
mixhub notify --session 1a2b3c # a short card about that session
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
To hear when an agent needs you or finishes a turn, add the agents' own hooks. `mixhub notify --print-hooks` prints both snippets with the full path to `mixhub`:
|
|
217
|
+
|
|
218
|
+
- Claude Code (`~/.claude/settings.json`): `Notification` and `Stop` hooks running `mixhub notify --hook claude`. You get the prompt Claude is waiting on, or the start of its last reply.
|
|
219
|
+
- Codex (`~/.codex/config.toml`): `notify = ["mixhub", "notify", "--hook", "codex"]`, which sends the last reply after each turn.
|
|
220
|
+
|
|
221
|
+
A hook sends to every configured channel, sends nothing when none is configured, and always exits 0.
|
|
178
222
|
|
|
179
223
|
## Language
|
|
180
224
|
|
|
@@ -189,9 +233,9 @@ The web workbench and its API listen on `127.0.0.1` only and have no login. They
|
|
|
189
233
|
- Other programs and other users on the same machine can reach it. Don't run MixHub on a shared machine you don't trust.
|
|
190
234
|
- Requests from websites in your browser are refused: the server checks the `Host` header (against DNS rebinding), `Origin` and `Sec-Fetch-Site`, only accepts JSON for anything that changes state, and sends a strict Content-Security-Policy.
|
|
191
235
|
- It only answers to `127.0.0.1` and `localhost`, so a reverse proxy or a forwarded port won't work, and shouldn't. To open it from another device, use an SSH tunnel (`ssh -L 8765:127.0.0.1:8765 your-machine`, then `http://localhost:8765`).
|
|
192
|
-
- Notification channels (Telegram, Slack,
|
|
236
|
+
- Notification channels (Telegram, Slack, webhooks) send session titles and message text to those services. Nothing is sent unless you configure a channel.
|
|
193
237
|
|
|
194
|
-
MixHub reads the agents' transcripts and databases without changing them. It writes to agent files only when you ask it to: renaming a session (the title goes where that agent keeps titles), workspace trust you opted into with `trust_all_workspaces` or `trust_roots` (so a resume doesn't stop at the trust prompt)
|
|
238
|
+
MixHub reads the agents' transcripts and databases without changing them. It writes to agent files only when you ask it to: renaming a session (the title goes where that agent keeps titles, also while the session runs: a title record appended to a Claude transcript, a row updated in the Codex or Gemini database), workspace trust you opted into with `trust_all_workspaces` or `trust_roots` (so a resume doesn't stop at the trust prompt) and `mixhub sync-rules` (writes `.gemini/GEMINI.md` from `AGENTS.md`).
|
|
195
239
|
|
|
196
240
|
## Configuration
|
|
197
241
|
|
|
@@ -202,13 +246,13 @@ Everything works without configuration. Optional settings live in a JSON file na
|
|
|
202
246
|
```json
|
|
203
247
|
{
|
|
204
248
|
"language": "auto",
|
|
205
|
-
"
|
|
206
|
-
"claude_theme": false,
|
|
249
|
+
"host_label": "work-laptop",
|
|
207
250
|
"trust_all_workspaces": false,
|
|
208
251
|
"trust_roots": ["~/workspace"],
|
|
209
252
|
"agy_mouse_shim": "auto",
|
|
210
253
|
"dispatch_scripts": ["my-agent-runner"],
|
|
211
|
-
"update_check": true
|
|
254
|
+
"update_check": true,
|
|
255
|
+
"auto_sleep_hours": 0
|
|
212
256
|
}
|
|
213
257
|
```
|
|
214
258
|
|
|
@@ -223,7 +267,7 @@ Everything works without configuration. Optional settings live in a JSON file na
|
|
|
223
267
|
| `MIXHUB_NO_OPEN=1` | `mixhub web` starts the daemon without opening a browser |
|
|
224
268
|
| `MIXHUB_NO_UPDATE_CHECK=1` | No daily update check |
|
|
225
269
|
|
|
226
|
-
`dispatch_scripts` names your own wrapper scripts that start agents headlessly, so their sessions are not mistaken for interactive ones.
|
|
270
|
+
`host_label` is the machine name shown in the web workbench (the hostname by default). `dispatch_scripts` names your own wrapper scripts that start agents headlessly, so their sessions are not mistaken for interactive ones. Sessions another agent started are hidden by what their CLI records (subagents, headless runs, Codex tasks whose `originator` is Claude Code). For older sessions without such a record, prompts that your own automation sends can be hidden with `data/local_rules.json` (`automated_prefixes`, `automated_substrings`, and `codex_automated_prefixes` for Codex only); `mixhub doctor --hidden` lists every session a text rule hides. Notes your own tools type into an agent on another agent's behalf go in `relayed_prefixes`: they never count as you having used a session, but don't hide one either.
|
|
227
271
|
|
|
228
272
|
## Architecture
|
|
229
273
|
|
|
@@ -235,24 +279,23 @@ Python standard library only. The code lives in the `mixhub` package:
|
|
|
235
279
|
| `mixhub/providers` | Per-agent launch and resume commands, quota reading, native title sync, harness presets |
|
|
236
280
|
| `mixhub/tui` | The curses sidebar, preview reader and window header (`HubTUI`, composed from one mixin per concern) |
|
|
237
281
|
| `mixhub/tmux` | The two tmux servers: outer workspace, the `hubpool` window pool, headers, home shell |
|
|
238
|
-
| `mixhub/handoff` | Handoff extraction and rendering
|
|
239
|
-
| `mixhub/channels` |
|
|
282
|
+
| `mixhub/handoff` | Handoff extraction and rendering |
|
|
283
|
+
| `mixhub/channels` | One-way notifications: Telegram Bot API, Slack incoming webhook |
|
|
240
284
|
| `mixhub/probe` | Reading an agent's screen: working, waiting for approval, menus |
|
|
241
285
|
| `mixhub/proc` | Process identity and classification, safe-kill guards |
|
|
242
286
|
| `mixhub/rules` | Shared agent rules compiler and workspace trust |
|
|
243
287
|
| `mixhub/filters/agy_claude` | Optional PTY filter for AGY (theme, cursor, mouse) |
|
|
244
288
|
| `mixhub/web` | Loopback HTTP API and assets for the web workbench |
|
|
245
289
|
| `mixhub/cli` | The `mixhub` command |
|
|
246
|
-
| `mixhub/core` | Shared paths, launch commands, preferences,
|
|
290
|
+
| `mixhub/core` | Shared paths, launch commands, preferences, session states |
|
|
247
291
|
|
|
248
292
|
State (pins, working set, caches, logs) lives in `~/.local/share/mixhub` for an installed package, or next to the code in a git checkout; set `MIXHUB_HOME` to put it elsewhere.
|
|
249
293
|
|
|
250
|
-
The workspace is two tmux servers. The outer session holds the sidebar on the left and, on the right, a client of
|
|
294
|
+
The workspace is two private tmux servers, neither of them your default one, so MixHub's sessions, options, key bindings and hooks never reach your own tmux. The outer session (`tmux -L mixhub`) holds the sidebar on the left and, on the right, a client of `tmux -L hubpool`, where every live agent runs in its own window. Switching sessions switches that window, so agents keep running untouched while you look elsewhere. Run `mixhub` inside your own tmux and the workspace opens nested in that pane.
|
|
251
295
|
|
|
252
296
|
## Roadmap
|
|
253
297
|
|
|
254
298
|
- An agent-facing command set (list sessions, read another session's recent turns) and a skill so agents can use MixHub themselves.
|
|
255
|
-
- Two-way chat channels: replies in a Telegram topic delivered into the matching session.
|
|
256
299
|
- Broader Linux polish.
|
|
257
300
|
|
|
258
301
|
## Development
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"language": "auto",
|
|
3
|
-
"
|
|
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
|
}
|