cokacmux

A terminal multiplexer for coding-agent sessions — browse, attach, clone, and resume conversations from Claude Code, Codex, and OpenCode in one place, backed by a provider-agnostic data model called UniversalType that lets the same in-memory representation be read or written in any agent’s native on-disk shape.

   codex   ──from_codex──▶   UniversalType   ──to_claude──▶   claude
                                ▲    │
                                │    ▼
                          opencode ↔ …

Three storage shapes, one unified surface:

Agent Storage
Claude Code JSONL files under ~/.claude/projects/<encoded-cwd>/<sid>.jsonl (+ optional tool-results/ sidecar)
Codex JSONL “rollout” files under ~/.codex/sessions/YYYY/MM/DD/, indexed by ~/.codex/state_5.sqlite::threads
OpenCode SQLite database at ~/.local/share/opencode/opencode.db (tables: session, message, part, …)

cokacmux normalizes all three into one model so the TUI can browse, preview, clone, attach, and resume sessions across providers from a single place. The same model is also exposed as a Rust library for other apps to embed.

Highlights

Install

Linux / macOS

curl -fsSL https://cokacmux.cokac.com/manage.sh | bash

The installer detects your OS/arch, downloads the matching prebuilt binary from dist_beta/, places it in /usr/local/bin (or ~/.local/bin if that isn’t writable), and adds a shell wrapper so cokacmux can cd you into the session’s working directory on exit. Run cokacmux to start.

Windows (PowerShell)

irm https://cokacmux.cokac.com/manage.ps1 | iex

Installs to %LOCALAPPDATA%\cokacmux\cokacmux.exe and adds that directory to your user PATH. Restart the terminal afterwards.

From source

git clone https://github.com/kstost/cokacmux
cd cokacmux
cargo build --release --bin cokacmux
./target/release/cokacmux

Requires Rust 1.74+ (uses 2021 edition). The opencode feature (default) pulls in a bundled SQLite via rusqlite, so a working C toolchain is required on first build; disable the feature if you only need JSONL providers (see Cargo features).

Cross-compilation

build.py orchestrates cross-compilation for Linux, macOS, and Windows using locally installed zig + cargo-zigbuild (for Linux/macOS) and cargo-xwin or GNU/LLVM toolchains (for Windows). Tools install under builder/tools/, never globally.

python build.py --setup            # install Rust + cross toolchains
python build.py --all              # build all platforms except Windows
python build.py --windows          # add Windows targets
python build.py --status           # show installed tools

On Windows hosts, build.py prefers Rust’s GNU/LLVM Windows toolchains when the MSVC linker isn’t available, avoiding the need for a Visual Studio Developer Prompt:

python build.py --setup-rust --no-color
python build.py --setup-windows --no-color
python build.py --windows --no-color

The Windows path installs Zig locally, generates the required GNU import libraries under builder/tools/winlibs/, and builds both windows-x86_64-gnullvm and windows-aarch64-gnullvm.

Run

cokacmux              # launch interactive TUI
cokacmux --check      # headless sanity check (no TTY needed)
cokacmux --version    # print version
cokacmux --help       # short usage summary

--check is useful in CI or smoke tests — it scans your ~/.claude, ~/.codex, and OpenCode database, prints the discovered session count and status, and exits without touching the terminal.

TUI keys

The TUI has two top-level views: the sessions view (default) and the agent view (active when attached to a running agent). Press Ctrl+] to flip between them; the agent stays alive in the background.

Sessions view

Key Action
q / Ctrl+Q quit
Tab / Esc switch focus between session list and preview
↑↓ / j k navigate list, or scroll preview when preview is focused
PgUp / PgDn jump 10 in list, or page preview when preview is focused
g / Home, G / End top / bottom of focused pane
Alt+↑ / Alt+↓ move selection within the agents sidebar
Alt+← / Alt+→ resize the sessions pane (saved to settings)
Enter toggle preview summary/full
/ filter by substring (id / cwd / title)
f cycle provider (ALL → claude → codex → opencode)
v toggle session list / tree view
t edit selected session’s title
r refresh from disk
c clone or cross-provider convert the selected session
d delete selected session (with confirm)
e / Ctrl+] attach (or start) the selected agent session
Ctrl+B toggle the agents sidebar visibility

The sessions pane opens in tree view by default — clone children appear under their parents. Press v to switch to a flat list. The choice is persisted in ~/.cokacmux/settings.json.

Agent view

When attached to a running agent (Claude Code / Codex / OpenCode):

Key Action
Ctrl+] detach back to the sessions view (agent keeps running)
Ctrl+K kill the selected / current agent
Ctrl+PgUp / Ctrl+PgDn cycle to the previous / next live agent

All other keys are forwarded to the agent’s terminal so the agent UI behaves exactly as if you ran it directly.

Configuration

cokacmux stores everything user-facing under ~/.cokacmux/:

Path Purpose
~/.cokacmux/settings.json UI preferences (pane sizes, view mode, debug)
~/.cokacmux/titles.json User-supplied title overrides per session
~/.cokacmux/agents/ Live-agent metadata + control sockets
~/.cokacmux/debug/*.log Debug logs (only written when debug is on)
~/.cokacmux/lastdir Last working directory (used by the shell wrapper)

settings.json schema

All fields are optional; defaults shown below.

{
  "cokacmux": {
    "sessions_pane_percent": 50,
    "sessions_pane_width": null,
    "agent_sidebar_width": 24,
    "agent_sidebar_visible": true,
    "session_view": "tree",
    "debug": false
  }
}

Unknown top-level keys are preserved on save, so settings files written by future versions remain compatible.

Debug logging

Debug logging is off by default. Turn it on by either:

When enabled, cokacmux writes plain-text logs to ~/.cokacmux/debug/*.log in the format [HH:MM:SS.mmm] message, split by subsystem:

Each file rotates at 5 MiB. Logging is best-effort and must never affect conversion or session correctness, so it is safe to leave on.

Library API

The TUI is just one consumer of the cokacmux crate — the library is the real surface. The public API is the same whether you use it from the binary, another Rust app, or your tests.

use cokacmux::{Provider, SessionSource, SessionTarget, read_session, write_session, convert};

// Read any provider's native format into the universal model.
let session = read_session(
    Provider::Claude,
    &SessionSource::Path("/path/to/session.jsonl".into()),
)?;

// Write it back out in any provider's native format.
write_session(
    Provider::Codex,
    &session,
    &SessionTarget::Path("/path/to/output.jsonl".into()),
)?;

// One-shot read+write across providers.
convert(
    Provider::Codex,
    Provider::OpenCode,
    &SessionSource::Path("rollout.jsonl".into()),
    &SessionTarget::OpenCodeDb { db_path: "opencode.db".into() },
)?;

In-memory string / connection helpers — useful for tests and for embedders that already hold their own I/O handles:

use cokacmux::providers;

// pure-string adapters (no library-managed I/O)
let session = providers::claude::from_jsonl_str(jsonl_text, &Default::default())?;
let out    = providers::codex::to_jsonl_string(&session, &Default::default())?;

// hand the library your own rusqlite::Connection so opencode.db isn't
// opened twice when your app already has it open
let session = providers::opencode::from_db_connection(&conn, "ses_xxx")?;

Install helpers write the universal session back into each agent’s native on-disk layout so that claude --resume <sid> / `codex resume

` / `opencode` pick the session up. These live behind the library API only — they are not exposed on any CLI surface, to keep accidents rare: ```rust use cokacmux::providers::claude::{self, install::InstallOpts}; claude::install::install_to_user_dir( &session, &InstallOpts { claude_home: None, overwrite: false }, )?; ``` `claude_home` (and `codex_home` / `db_path` on the other providers) is overridable so tests can target a tempdir. ## Cargo features cokacmux is feature-gated so you only compile what you need: | Feature | Default | Pulls in | |---|---|---| | `claude` | ✓ | — | | `codex` | ✓ | — | | `opencode` | ✓ | `rusqlite` (bundled SQLite + a C toolchain) | | `cli` | ✓ | `clap`, `anyhow` (also enables `discovery`) | | `discovery` | ✓ | `dirs` — for locating `~/.claude`, `~/.codex`, etc. | | `tui` | ✓ | `ratatui`, `crossterm`, `portable-pty`, `vt100`, `unicode-width` — for the `cokacmux` binary | Library-only consumers can drop everything they don't need: ```toml [dependencies] cokacmux = { version = "0.1", default-features = false, features = ["claude", "codex"] } ``` Dropping `opencode` skips the SQLite C compile entirely. Dropping `tui` removes the ratatui/crossterm dependency tree. ## Guarantees - **Same-provider round-trip is bit-identical.** `from_X → to_X` of any Claude or Codex JSONL produces the exact original bytes back, because each `UMessage` keeps its original record under `provenance.raw` and `to_X` replays it when the source provider matches. - **Cross-provider conversion preserves semantic conversation payloads.** The 6 ordered provider-to-provider conversions are measured with native Claude / Codex / OpenCode fixtures covering cwd, visible text, reasoning, tool calls, and tool results. - **No silent drops.** Unknown event types become `ContentBlock::Other` blocks tagged with the source type — they survive round-trips even when cokacmux doesn't know what they mean. - **Intentional diffs are documented.** See [`docs/INTENTIONAL_DIFFS.md`](/docs/INTENTIONAL_DIFFS.html) for the exhaustive list of cases where the converted artifact deliberately differs from the source (e.g. runtime preludes that must not be re-emitted as new conversation turns). ## Project layout ``` src/ ├── lib.rs Public API + SessionSource/Target enums ├── bin/cokacmux.rs TUI multiplexer (the binary) ├── universal/ UniversalSession + validate ├── providers/ │ ├── claude/ read, write, install, path encoder, sidecar │ ├── codex/ read, write, install │ ├── opencode/ read, write, install, db (rusqlite) │ └── discovery.rs Scan ~/.claude, ~/.codex, opencode.db ├── session/ Clone tree, title overrides, native validate ├── pivot.rs X → Universal → Y composition ├── error.rs, ids.rs, time.rs, jsonl.rs docs/ ├── PLAN.md Full design rationale + history ├── RESULTS.md Measured smoke-test outputs ├── INTENTIONAL_DIFFS.md Documented conversion diffs └── SESSION_CLONE_NATIVE_FIDELITY_STRATEGY.md Native-clone fidelity strategy tests/ ├── roundtrip.rs Same-provider fidelity tests ├── pivot.rs Cross-provider preservation tests ├── install.rs Tempdir install layout tests ├── session.rs Session model unit tests └── live_acceptance.rs Optional live-corpus acceptance builder/ Cross-compilation orchestration (Python) build.py Top-level build CLI manage.sh, manage.ps1 One-line installers dist_beta/ Prebuilt binaries for each target ``` ## Documentation - [`docs/PLAN.md`](/docs/PLAN.html) — full design rationale, provider-by-provider storage layout, mapping tables, and the questions / decisions that shaped the design. - [`docs/RESULTS.md`](/docs/RESULTS.html) — measured outputs of running this on the maintainer's real session corpus. - [`docs/INTENTIONAL_DIFFS.md`](/docs/INTENTIONAL_DIFFS.html) — categorized list of cases where converted output deliberately differs from native. - [`docs/SESSION_CLONE_NATIVE_FIDELITY_STRATEGY.md`](/docs/SESSION_CLONE_NATIVE_FIDELITY_STRATEGY.html) — strategy for cross-provider clone that resumes correctly under each agent's native runtime. ## License MIT. Author: cokac <monogatree@gmail.com>.