Metadata-Version: 2.5
Name: mixhub-cli
Version: 0.2.0
Summary: One switchboard for Claude Code, Gemini (AGY), Codex and OpenCode sessions: a tmux workspace, a web workbench and handoff between agents.
Project-URL: Homepage, https://github.com/ShinoKana/mixhub
Project-URL: Repository, https://github.com/ShinoKana/mixhub.git
Project-URL: Issues, https://github.com/ShinoKana/mixhub/issues
Author: MixHub Contributors
License: MIT
License-File: LICENSE
Keywords: ai-agents,claude-code,codex,gemini,handoff,opencode,terminal,tmux
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Python: >=3.10
Description-Content-Type: text/markdown

<div align="center">

# MixHub

**One switchboard for Claude Code, Gemini (AGY), Codex and OpenCode sessions**

[![License: MIT](https://img.shields.io/badge/license-MIT-596F62.svg)](#license)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776AB.svg)](#install)
[![Dependencies: stdlib](https://img.shields.io/badge/python_dependencies-stdlib_only-596F62.svg)](#architecture)

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.

</div>

```text
┌─ SIDEBAR (32 cols) ────────────┬─ RIGHT PANE (PREVIEW OR LIVE CLI) ───────────────────────────┐
│  ⌄  mixhub              ‹ ›  ◧ │  ~/workspace/project · a1b2c3d4                              │
│                                │  ◇ Auth flow                                                 │
│  ✎  New session                │  ──────────────────────────────────────────────────────────  │
│  ▾  Pinned 1                   │                                                           ─  │
│  ◇  Auth flow               ★  │  > Refactor the state machine and run the tests           ━  │
│  ▾  Active 2                   │    Edited rules_sync.py (+42 -8)                          ─  │
│  ◈  Payment webhook        2m  │    Ran 43 tests in 0.14s · OK                             ─  │
│  ◇  API rate limit         5m  │                                                           ─  │
│  ▾  Recent 4                   │  > Check idempotency on retry                             ━  │
│  ·  Database migration     3h  │    Verified retry keys and added unit coverage.           ─  │
│  ·  OAuth token refresh    1d  │                                                              │
│  claude 83% 03:00   60% 10/30  │  ──────────────────────────────────────────────────────────  │
│  codex  69% 04:25   41% 10/31  │   [ Resume ↵ ]  [ Go to Tab ]                                │
│  gemini 86% 02:29   21% 11/01  │                                                              │
│  ▴ cpu 12%  mem 64%     ↗ web  │                                                              │
└────────────────────────────────┴──────────────────────────────────────────────────────────────┘
```

## Why

Agent CLIs keep getting better at what happens inside one session. What they don't do is look across sessions: which of my twelve sessions is waiting for me, which one finished while I was away, which agent still has quota left, and how do I hand a half-done task to another agent. MixHub covers that part and nothing else:

- It reads the native transcript files and databases in place. There is no import step and no second copy of your history; `claude --resume <id>` keeps working as before.
- The right pane runs the unmodified CLI. When an agent ships an update, you get it in MixHub without waiting for MixHub.
- It only adds things that stay useful as agents improve: a cross-agent index, a process pool, quota at a glance, handoff between agents, and notifications.

## Install

Requirements: Python 3.10+ and `tmux` (developed on tmux 3.6).

| Platform | Status |
| :--- | :--- |
| macOS | Supported |
| Linux, Windows via WSL | Experimental: the workspace, web workbench and CLI work; Terminal.app tab switching and the Dock badge are macOS-only |
| Native Windows | Not supported; run MixHub inside WSL |

```bash
curl -fsSL https://raw.githubusercontent.com/ShinoKana/mixhub/main/install.sh | sh
```

The script installs the `mixhub-cli` package (it provides the `mixhub` command) with uv, pipx or a private venv (whichever it finds first) and tells you if Python 3.10+ or tmux is missing. Installing it yourself works the same: `uv tool install mixhub-cli` or `pipx install mixhub-cli`.

```bash
mixhub           # open or reattach to the workspace
mixhub web       # local web workbench at http://127.0.0.1:8765
mixhub doctor    # check providers, channels, locks and terminal settings
```

From a clone:

```bash
git clone https://github.com/ShinoKana/mixhub.git
ln -sf "$PWD/mixhub/bin/mixhub" ~/.local/bin/mixhub
```

### Updating

```bash
mixhub update          # upgrade, then reload the sidebar, reader and web daemon on the new code
mixhub update --check  # only say whether a newer version exists
```

`mixhub update` uses whatever installed MixHub: `uv tool upgrade`, `pipx upgrade`, `pip install --upgrade` inside install.sh's venv, or `git pull --ff-only` in a clone. Running agent sessions are not interrupted. MixHub never updates itself: once a day it asks PyPI for the latest version in the background and, when there is a newer one, prints a one-line hint from `mixhub web`, `mixhub doctor` and the workspace's exit message. Set `"update_check": false` in Preferences or `MIXHUB_NO_UPDATE_CHECK=1` to turn the check off.

## Supported agents

| Agent | Sessions read from | Resume |
| :--- | :--- | :--- |
| Claude Code | `~/.claude/projects/**.jsonl` | `claude --resume <id>` |
| Gemini via AGY | AGY conversation store | `agy --conversation <id>` |
| Codex | `~/.codex/sessions/` rollouts + state DB | `codex resume <id>` |
| OpenCode | `opencode.db` (read-only) | `opencode -s <id>` |

`✎ 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.

## The workspace

**Sidebar.** Sessions are grouped into `Pinned`, `Active` (sessions you are working with) and `Recent`. A row shows the agent's brand-colored badge, the title the agent itself gave the session, and its age. The badge animates while the agent is working and turns into a waiting mark when it needs approval or an answer. Unread replies are bold. `▾ mixhub` filters by agent, `‹ ›` walks back and forward through what you looked at, `◧` collapses the sidebar. Hover a row to pin it. The sidebar is entirely click-driven; keys go to the agent.

**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.

**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).

**Footer.** Remaining `5h` and weekly quota with reset times for each agent that has at least one session on this machine, plus CPU and memory. Codex quota comes from its own session files. For exact Claude Code and Gemini (AGY) numbers, point their status line at MixHub, which records the quota each agent reports and prints a short status line back:

```jsonc
// ~/.claude/settings.json
"statusLine": {"type": "command", "command": "mixhub statusline claude"}

// ~/.gemini/antigravity-cli/settings.json
"statusLine": {"type": "command", "command": "mixhub statusline agy", "enabled": true}
```

Without it the footer estimates Claude's quota from transcripts.

**OpenCode tabs.** OpenCode keeps several sessions in one process. MixHub follows the tab you are on: the window is renamed to that session, and other tabs show a preview and open in their own window when resumed.

**Attention.** When a session finishes a turn or starts waiting while you are elsewhere, MixHub rings the outer terminal once, so Terminal.app shows a Dock badge and a counter in the window title.

**Crash recovery.** `mixhub shell-hook` prints a zsh snippet for `~/.zshrc`. If Terminal restores the window MixHub was in after a crash or forced quit, the snippet reattaches it.

## Session lifecycle

MixHub separates whether a session is listed as `Active` from whether its process is running. Idle live sessions are put to sleep (process stopped, session kept in `Active`) and resumed in place when you click them:

| Condition | Idle time before sleep |
| :--- | :--- |
| Normal | 12 hours |
| Moderate memory pressure | 6 hours |
| Critical memory pressure, or more than 6 live sessions | 4 hours |

A session is never put to sleep while it is working or waiting for approval, has running subagents or open background tasks, or has user processes running on its terminal. If the process table can't be read, nothing is put to sleep. `/exit` in the agent moves the session to `Recent`; a system shutdown keeps `Active` as it was.

## Handoff between agents

When one agent runs out of quota or you want a second opinion, `mixhub handoff` writes a compact handoff bundle (under 2,000 tokens, ten sections: goal, acceptance criteria, decisions, rejected approaches, your verbatim constraints, git and file state, verification commands, open questions, next action, evidence pointers) plus a machine-readable `handoff.yaml`, and starts the target agent with it:

```bash
mixhub handoff SESSION_ID --to codex --mode delegate
mixhub handoff SESSION_ID --to claude --mode review
mixhub handoff SESSION_ID --to agy --dry-run
```

The new session is titled `↳ <source title>`. A write lease per working tree records which session is allowed to write, and a relay to a busy session is held until its turn ends.

## Web workbench and notifications

<picture>
  <source media="(prefers-color-scheme: dark)" srcset=".github/assets/web-dark.png">
  <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">
</picture>

- `mixhub web` serves a loopback-only web UI: search across all sessions, full transcripts, and a phone-friendly bar for approving or stopping a waiting session.
- `mixhub notify` and the attention hook can send to Telegram, Discord, Slack (Socket Mode bridge), Feishu/Lark, or any webhook or command, with `always`, `auto_idle` and `manual` modes.

## Language

The web workbench, terminal messages and handoff bundles are in English, Chinese or Japanese. Terminal text and handoff bundles use `MIXHUB_LANGUAGE`, else `"language"` in Preferences (`"auto"`, `"en"`, `"zh"`, `"ja"`), else your locale. The web workbench uses the language picked in its settings (`,`), else the Preferences language, else your browser's. Notifications follow the language you write to the agent in. Text missing in a language falls back to English.

Translations are one JSON file per language in [`mixhub/i18n/`](mixhub/i18n). To add a language, copy `en.json` to `<code>.json`, translate the values (keep the `{placeholders}`), and add the code to `LANGUAGES` in `mixhub/i18n/__init__.py`. Partial translations are fine.

## Security

The web workbench and its API listen on `127.0.0.1` only and have no login. They can read every transcript MixHub sees and type into running agent sessions, so treat the port like a shell:

- Other programs and other users on the same machine can reach it. Don't run MixHub on a shared machine you don't trust.
- 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.
- 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`).
- Notification channels (Telegram, Slack, Discord, Feishu, webhooks) send session titles and message text to those services. Nothing is sent unless you configure a channel.

MixHub reads the agents' transcripts and databases without changing them. It writes to agent files only when you ask it to: renaming a session (the title goes where that agent keeps titles), workspace trust you opted into with `trust_all_workspaces` or `trust_roots` (so a resume doesn't stop at the trust prompt), `mixhub slim --apply` (replaces old screenshots in Claude transcripts with a placeholder) and `mixhub sync-rules` (writes `.gemini/GEMINI.md` from `AGENTS.md`).

## Configuration

MixHub keeps its state (`data/`, `Preferences`, `hub_state.json`, the Web daemon's `.hub.pid` / `hub.log`) in one directory: `$MIXHUB_HOME` if set, otherwise the repository root when you run from a clone, otherwise `$XDG_DATA_HOME/mixhub` or `~/.local/share/mixhub`. Nothing is written next to an installed package.

Everything works without configuration. Optional settings live in a JSON file named `Preferences`, looked up at `$MIXHUB_PREFERENCES_PATH`, then `~/.config/mixhub/Preferences`, then the state directory:

```json
{
  "language": "auto",
  "user_name": "Developer",
  "claude_theme": false,
  "trust_all_workspaces": false,
  "trust_roots": ["~/workspace"],
  "agy_mouse_shim": "auto",
  "dispatch_scripts": ["my-agent-runner"],
  "update_check": true
}
```

[`Preferences.example`](Preferences.example) lists every option. Environment variables:

| Variable | Effect |
| :--- | :--- |
| `MIXHUB_HOME` | State directory |
| `MIXHUB_PREFERENCES_PATH` | Preferences file |
| `MIXHUB_PORT` | Web workbench port (default `8765`) |
| `MIXHUB_LANGUAGE` | Interface language, over Preferences and the locale |
| `MIXHUB_NO_OPEN=1` | `mixhub web` starts the daemon without opening a browser |
| `MIXHUB_NO_UPDATE_CHECK=1` | No daily update check |

`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`).

## Architecture

Python standard library only. The code lives in the `mixhub` package:

| Package | Responsibility |
| :--- | :--- |
| `mixhub/hub` | Session discovery per agent, transcript parsing, process binding, lifecycle and sleep guards, working set |
| `mixhub/providers` | Per-agent launch and resume commands, quota reading, native title sync, harness presets |
| `mixhub/tui` | The curses sidebar, preview reader and window header (`HubTUI`, composed from one mixin per concern) |
| `mixhub/tmux` | The two tmux servers: outer workspace, the `hubpool` window pool, headers, home shell |
| `mixhub/handoff` | Handoff extraction and rendering, write leases, hold queue, clean forks |
| `mixhub/channels` | Notification adapters, approval ledger, away receipts, Slack bridge |
| `mixhub/probe` | Reading an agent's screen: working, waiting for approval, menus |
| `mixhub/proc` | Process identity and classification, safe-kill guards |
| `mixhub/rules` | Shared agent rules compiler and workspace trust |
| `mixhub/filters/agy_claude` | Optional PTY filter for AGY (theme, cursor, mouse) |
| `mixhub/web` | Loopback HTTP API and assets for the web workbench |
| `mixhub/cli` | The `mixhub` command |
| `mixhub/core` | Shared paths, launch commands, preferences, plugin protocols |

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.

The workspace is two tmux servers. The outer session holds the sidebar on the left and, on the right, a client of a private `tmux -L hubpool` server where every live agent runs in its own window. Switching sessions switches that window, so agents keep running untouched while you look elsewhere.

## Roadmap

- An agent-facing command set (list sessions, read another session's recent turns) and a skill so agents can use MixHub themselves.
- Two-way chat channels: replies in a Telegram topic delivered into the matching session.
- Broader Linux polish.

## Development

```bash
python3 -m pytest -q
```

`python3 scripts/demo_seed.py ~/.cache/mixhub-demo --web 8799` serves the web workbench on made-up sessions, which is how the screenshots are taken.

The tests never touch your real sessions or tmux servers: every state path is redirected to a temp directory and tmux tests use private sockets. `tests/test_oss_desensitize_and_cc_ds.py` checks every tracked file for personal paths and names.

## License

MIT
