AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
Terminal manager for every OS
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A terminal workspace manager for Linux, Windows, and macOS — a lightweight, project-oriented alternative to terminal multiplexers like tmux, built as a single native desktop app. Group your terminals into named workspaces (one per project), split each into multiple resizable panels, connect to remote machines over SSH, and get a desktop notification when a long-running task finishes.README 전문 보기 ↗A terminal workspace manager for Linux, Windows, and macOS — a lightweight, project-oriented alternative to terminal multiplexers like tmux, built as a single native desktop app. Group your terminals into named workspaces (one per project), split each into multiple resizable panels, connect to remote machines over SSH, and get a desktop notification when a long-running task finishes.
umux watches the terminal byte stream for the completion signals emitted by AI CLI tools (Claude Code, Aider, etc.) — the standard OSC 9;9 / OSC 99 / OSC 777 escape sequences — and fires a native desktop notification when such a task completes, so you can step away while code is being generated.
Platform: Linux (Ubuntu/Wayland), Windows 10+, and macOS 11+. Releases carry installers for all three platforms: Linux (
.deb/.AppImage/.rpm), a universal macOS.dmg, and a Windows NSIS.exe.Stack: Tauri v2 (Rust backend) + React + TypeScript (frontend), rendering its own embedded terminal via xterm.js.
Ctrl+Shift+T adds one, double-click renames, right-click pins), and any tab's area splits into as many resizable panels as you need (drag the dividers). A sensible minimum size is enforced so no panel can collapse to nothing. Expand any panel to fill its tab with pane zoom — Ctrl+Shift+Z or the zoom button in the panel's corner; the same action restores the exact previous layout, and the covered panels keep running underneath.vim, htop, and fzf render correctly. Each panel opens an interactive shell (your $SHELL by default; PowerShell on Windows).umux runs on Linux (Ubuntu/Wayland) and macOS (11+, universal binary); Windows (.exe installer) arrives with the v1.0.0 release. There are two ways to install it:
.deb or .AppImage from GitHub Releases — no compiler or toolchain needed.Prebuilt packages are published on the GitHub Releases page:
👉 https://github.com/CrystalPlatforms/umux/releases
Each release provides two Linux package formats. Pick one:
.deb (integrates with the system)The .deb is the native Ubuntu package. It installs umux into your application menu, adds the umux command, and registers the desktop icon.
Download the umux_<version>_amd64.deb file from the release.
Install it (and its dependency, WebKit):
sudo apt update
sudo apt install -y ./umux_*_amd64.deb
Installing via
apt(instead ofdpkg) lets the system pull in the requiredlibwebkit2gtk-4.1dependency automatically. If you useddpkg -iand hit dependency errors, runsudo apt --fix-broken install.
Launch umux from your application menu, or run umux in a terminal.
To update later, just download the new .deb and repeat step 2.
To uninstall:
sudo apt remove umux
.AppImage (portable, no install)The .AppImage is a single portable file — no installation, no root permissions. It runs on most modern Linux distributions.
Download the umux_<version>_amd64.AppImage file from the release.
Make it executable:
chmod +x umux_*_amd64.AppImage
Run it:
./umux_*_amd64.AppImage
First run: if nothing happens or you see a dialog about "AppImage" support, install AppImageLauncher or run it from a terminal to see the error. On Ubuntu you may also need
libwebkit2gtk-4.1-0installed (sudo apt install libwebkit2gtk-4.1-0).
To update, just download the new .AppImage and replace the old file. To uninstall, simply delete the file.
Each release ships a universal macOS image (umux_<version>_universal.dmg, Apple Silicon + Intel in one file). A Windows installer (umux_<version>_x64-setup.exe) arrives with the v1.0.0 release. Download, run the installer / drag to Applications, done.
Unsigned builds: umux is free open source and uses no paid signing certificates. On first run, macOS will report an "unidentified developer" — right-click the app and choose Open (or run
xattr -cr /Applications/umux.appin a terminal). On Windows, SmartScreen may show a blue warning — click More info → Run anyway. This only happens once.
This is a one-time setup — follow the steps in order. Every command below is run in a terminal. If a step fails, read the error message; the most common problems (missing pkg-config, wrong WebKit version) are flagged in the notes.
The Tauri v2 backend compiles against native WebKit/GTK libraries, so first install the build dependencies. Update the package index, then install everything in one go:
sudo apt update
sudo apt install -y \
pkg-config \
libwebkit2gtk-4.1-dev \
build-essential \
libssl-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
libxdo-dev \
libnotify-bin
What each one is for:
pkg-config — lets Rust locate the native libraries below. This is the single most common missing piece on a fresh clone; without it the build fails with The pkg-config command could not be found.libwebkit2gtk-4.1-dev — the web engine Tauri renders into. ⚠️ Tauri v2 needs 4.1, not 4.0 (that was Tauri v1). Installing the wrong one produces cryptic linker errors.build-essential — C/C++ compiler (gcc, g++, make) needed to compile native crates.libssl-dev — OpenSSL headers for TLS support.libayatana-appindicator3-dev — system-tray support.librsvg2-dev — SVG rendering (app icons).libxdo-dev — input simulation for keyboard shortcuts.libnotify-bin — provides notify-send, used for desktop completion notifications. (Usually preinstalled, but listed for completeness.)umux needs a stable Rust toolchain (1.77.2 or newer). Check whether you already have it:
rustc --version
If that prints a version ≥ 1.77.2, skip to Step 3. If it says command not found, install Rust via rustup (the official installer):
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Follow the prompts (the defaults are fine), then load Rust into your current shell:
source "$HOME/.cargo/env"
Verify it works:
rustc --version # should now print a version number
cargo --version # Rust's build tool
The frontend needs Node.js 18 or newer. Ubuntu's default node is often older, so check first:
node --version
npm --version
If Node is missing or older than 18, install it via NodeSource (recommended over the Ubuntu package, which tends to be outdated):
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
sudo apt install -y nodejs
Re-run node --version to confirm you're on 18+.
Clone the repository (or fork it first if you plan to contribute):
git clone https://github.com/CrystalPlatforms/umux.git
cd umux
Pull in the frontend libraries once:
npm install
This creates node_modules/. You only need to re-run it after updating dependencies (package.json changes).
To launch the desktop window with hot-reloading (this compiles the Rust backend and starts the Vite dev server together — the first build can take several minutes):
npm run tauri dev
The umux window should appear. You're set up.
For a production / release build (an installable Ubuntu package):
npm run tauri build
Artifacts land in src-tauri/target/release/bundle/. Note: because releases now carry signed update bundles, a local tauri build requires the signing key — export TAURI_SIGNING_PRIVATE_KEY and TAURI_SIGNING_PRIVATE_KEY_PASSWORD first (one-time setup: Updates → Maintainers). tauri dev needs no key. Install the .deb with:
sudo apt install ./src-tauri/target/release/bundle/deb/*.deb
Then launch umux from your application menu, or run umux in a terminal.
The pkg-config command could not be found — you skipped Step 1. Run the apt install line again.webkit2gtk — you installed the 4.0 version instead of 4.1. Remove it and install libwebkit2gtk-4.1-dev (Step 1).error: failed to run custom build command for ... openssl — install libssl-dev and pkg-config (Step 1).npm: command not found — Node.js isn't installed or your shell didn't pick it up. Re-run Step 3 and open a new terminal.Already set up? These are the everyday commands:
npm install # one-time, after cloning or pulling dependency updates
npm run tauri dev # development: desktop window with hot-reload
npm run dev # frontend only (Vite dev server, http://localhost:5173)
cd src-tauri
cargo check # fast Rust type-check (no binary produced)
cargo test # backend unit tests
The frontend test suite uses Vitest + Testing Library:
npm test # single run
npm run test:watch # watch mode
umux is a two-process Tauri app. The Rust backend (src-tauri/src/) owns everything OS- and I/O-bound:
pty_service — pseudoterminal lifecycle (open/write/resize/close) and a per-handle output stream.osc_parser — a stateful byte-stream parser that recognizes completion sequences (across chunk boundaries) and passes all other bytes through untouched. Normal terminal output is byte-identical whether or not the parser is active.ssh_manager — PTY-backed shells over SSH, reusing the same output-stream abstraction so local and remote panels are indistinguishable to the frontend.notification_service — turns parsed OSC events into desktop notifications (debounced, mute-aware).workspace_store — reads/writes workspace definitions to the per-OS config directory; a corrupted config falls back to defaults rather than crashing.The React + TypeScript frontend (src/) renders the terminal and workspace UI and talks to the backend through Tauri commands and event channels.
Workspace definitions (names, order, panel layout, working directories, SSH targets) are persisted to:
Linux: $XDG_CONFIG_HOME/umux/workspaces.json
~/.config/umux/workspaces.json (default, when XDG_CONFIG_HOME is unset)
Windows: %APPDATA%\umux\workspaces.json (from v1.0.0)
macOS: ~/Library/Application Support/umux/workspaces.json
On macOS the config directory is ~/Library/Application Support/umux; on first launch after the change, umux automatically moves workspaces.json and settings.json from the old ~/.config/umux location, so nothing is lost.
If the file is missing, umux starts fresh. If it is corrupt, umux falls back to default workspaces and shows a warning rather than failing to launch.
umux detects finished AI-CLI tasks purely through OSC escape sequences in the terminal stream (OSC 9 / 99 / 777). Claude Code emits those notification sequences automatically only in Ghostty, Kitty, and iTerm2 — in any other terminal (umux panels included) it stays silent by default, so you get neither the desktop notification nor the needs-attention status dot.
To turn the signals on, set Claude Code's notification channel to iterm2 (it emits the iTerm2-style OSC 9 sequence, which umux parses directly):
// ~/.claude/settings.json
{ "preferredNotifChannel": "iterm2" }
This is machine-local and reversible ("auto" restores the default). Other AI CLIs that print OSC 99 / 777 notifications work with no configuration.
umux reports a single anonymous event — app_open at startup — to Aptabase, so development is guided by how many people actually installed and use the app (Aptabase counts unique users per event). Nothing else is ever sent: no terminal content, commands, workspace names, file paths, or any other user data — the one event carries no payload at all. Analytics is always on with no switch and no opt-out (deliberate product decision, 2026-09-12: the old analyticsEnabled kill switch was removed from the settings schema — a stale key in an old settings.json is simply ignored).
umux Storestation is an optional background daemon — off by default — that owns umux's terminal sessions. With it on, closing every umux window no longer kills your work: agents and long jobs keep running headlessly, and umux attach brings the view back. With it off, the desktop app behaves exactly like it always has.
umux attach launches (or focuses) the umux app bound to Storestation's living sessions. Storestation must be running — offline attach tells you so and exits.4)..deb / .AppImage / .rpm / NSIS .exe / .dmg) beside the umux CLI — enabling Storestation never means installing something extra. A normal app update replaces the daemon binary too; its version stays locked to the app's.The umux CLI drives Storestation headlessly — these commands talk to the daemon's local socket (see the protocol spec):
umux status # daemon health — offline is a state, not an error (exit 0)
umux status --json # machine-readable: running, version, pid, uptime, sessions, attachedClients
umux sessions list # the live sessions Storestation owns (offline → empty list)
umux sessions list --json --limit 50
umux agent-context # machine-readable description of this CLI (schema 1)
umux attach # relaunch/focus the app on Storestation's sessions
umux attach --dry-run # print the resolved app path, launch nothing
umux-storestation run # serve the socket in the foreground (what autostart launches)
umux-storestation stop # graceful shutdown — idempotent (offline exits 0)
Where the daemon binary lives mirrors the CLI exactly: /usr/bin on .deb systems, the NSIS install directory (already on your user PATH) on Windows, and beside the app inside umux.app/Contents/MacOS/ on macOS — one PATH line covers both (see umux on your PATH; the plain-CLI install.sh installs the daemon alongside too). Exit codes follow the shared catalog (0 ok/offline, 2 usage, 3 unreachable, 4 already running, 5 internal).
Trying it out: point
UMUX_CONFIG_DIRat a scratch directory to isolate the test daemon's store, socket and PID files from your daily umux — see the Storestation protocol spec for the full walkthrough.
Zero-cost policy unchanged: builds stay unsigned, so the SmartScreen / Gatekeeper first-run notes in Option A apply to the daemon binary exactly as to everything else.
umux)umux ships a small command-line tool for scripting and offline work. It reads and writes the same store files as the app through the same library, so a CLI write and an app write can never disagree about the format.
umux on your PATHThe CLI ships inside the same installers as the app — how you get it on your PATH depends on how you installed umux:
| Install method | umux availability |
|---|---|
Windows (NSIS .exe) | Automatic. The installer adds the install directory to your user PATH — open a new terminal and umux --version works. (Terminals that were already open keep their old PATH.) Uninstalling removes the PATH entry. |
Linux (.deb) | Automatic. The package installs /usr/bin/umux — works in any fresh shell. |
macOS (.dmg) | One line. The CLI lives beside the app binary inside the bundle; add it to your PATH once: |
Linux (.AppImage) | Not persistent by design — an AppImage cannot modify your PATH. See the workaround below. |
macOS PATH setup (then open a new terminal):
echo 'export PATH="/Applications/umux.app/Contents/MacOS:$PATH"' >> ~/.zshrc
AppImage workaround (extract once, link the binary somewhere already on your PATH):
./umux_*_amd64.AppImage --appimage-extract # → squashfs-root/
mkdir -p ~/.local/bin
ln -sf "$PWD/squashfs-root/usr/bin/umux" ~/.local/bin/umux
# make sure ~/.local/bin is on your PATH (add to ~/.bashrc if needed):
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
Build it from source instead (it is part of the Cargo workspace). Or install it without the app straight from the latest release (macOS, Linux x86_64 — review with -- --dry-run appended):
curl -fsSL https://raw.githubusercontent.com/CrystalPlatforms/umux/main/install.sh | sh
Note for contributors: src-tauri/binaries/ (the sidecar copies used by the installers — the umux CLI and the umux-storestation daemon) is a gitignored build artifact — if tauri dev ever complains about them, generate them once with node scripts/bundle-cli.mjs:
cd src-tauri
cargo build --release --package umux --package umux-storestation
# → src-tauri/target/release/umux + umux-storestation
Every command that touches a store needs a target: --desk (the desktop app's store) or --term (the terminal-UI store, whose TUI ships in v1.9.0).
umux list --desk # saved workspaces as JSON
umux new myproject --desk # create an empty workspace
umux rename old new --desk # rename a workspace
umux rm myproject --desk # delete a workspace
umux split myproject --desk # split its layout side-by-side (add --vertical for top/bottom)
umux config get --desk # all settings as JSON
umux config set notifications-enabled true --desk
umux export --desk -o backup.json # export the store (see Exchange format below)
umux export --desk # …or print the document to stdout
umux import cmux # import from the cmux app's saved files
umux import cmux --dry-run # …preview the plan, write nothing
umux import umux backup.json --desk # restore an export (REPLACES the store)
umux notify "build finished" # desktop notification, no app needed
umux notify uses the same notification mechanism as the app (notify-send on Linux, Notification Center via osascript on macOS, a Windows toast). If the platform's notification system is unavailable, it prints a clear error and exits non-zero. Point UMUX_CONFIG_DIR at a directory to move the whole store root (handy for testing).
macOS note (CLI only): a banner from
umux notifyis attributed to Script Editor — macOS attributes every AppleScript-sent notification to its runtime, and a bare CLI process cannot use the native Notification Center (the system refuses unbundled processes). Title and text are correct; on Linux the sender shows as umux. The umux app itself shows proper umux attribution on macOS (native Notification Center from the bundled app, with an automaticosascriptfallback duringtauri dev). Shipping a tiny helper.appfor the CLI (the terminal-notifier approach) was evaluated and deliberately deferred — it adds a second bundle to every install path plus a permission prompt for a cosmetic gain.
umux export writes a neutral, self-describing JSON document — the format umux import umux restores and Desktop↔Terminal transfer (v1.9.0) reads back. Settings are deliberately not part of it: they are per-app configuration, not state you move between surfaces.
{
"format": "umux-exchange",
"version": 1,
"kind": "workspaces",
"data": { "workspaces": [ ... ], "groups": [ ... ], "order": [ ... ] }
}
| Field | Meaning |
|---|---|
format | Always "umux-exchange" — anything else is not an umux exchange document. |
version | Format version, currently 1. Readers must refuse unknown versions with a clear message — never guess. |
kind | What data holds: "workspaces" (the store's workspace state: workspaces, groups, order). |
data | The store's own serialized shape — the exact JSON workspaces.json uses — so export → import round-trips unchanged. |
umux import umux <file> [--desk|--term] replaces the chosen store with the document's state — export → import into a fresh store reproduces the original state exactly (id for id). Use --dry-run to preview. Malformed documents and unknown versions are refused with a clear error and the store is left untouched.
umux import cmux reads the cmux app's saved files (its cmux.json config and live session store) and imports them into the chosen store: workspaces, sidebar order, groups with membership, working directories, and one named tab per cmux surface. Nothing is ever overwritten — a name that already exists gets a from cmux suffix (numbered further when taken: X from cmux, X from cmux 2, …). The cmux files are read strictly read-only. --dry-run prints the plan (the collision-resolved tree) and writes nothing. Not available on Windows. The same import powers the in-app wizard (Settings → Import from cmux); a shared-fixture test suite keeps the two implementations at parity.
umux keeps itself current through GitHub Releases only — there is no update server (zero-cost policy). The flow:
Signature verification is enforced: every update bundle is signed, and umux refuses to install anything whose signature does not verify against its built-in public key. This is why update bundles can be trusted even though the regular installers are unsigned (first-run warnings still apply to those — see A.3).
Supported update channels per platform: macOS (universal .app bundle), Windows (NSIS installer), Linux (.AppImage). The .deb package updates the classic way — install the newer .deb over the old one.
The release pipeline signs update bundles automatically. One-time setup (done once per machine that builds releases):
Generate the free signing key (no paid certificate involved):
npm run tauri signer generate -- -w ~/.tauri/umux.key
This prints a public key and writes the private key to ~/.tauri/umux.key (the public key is saved right next to it as ~/.tauri/umux.key.pub, so it is never lost). The password is optional — an empty password keeps CI simpler. Re-running the command needs --force (Tauri refuses to overwrite an existing key).
Paste the public key into src-tauri/tauri.conf.json under plugins.updater.pubkey (it replaces the PASTE_PUBLIC_KEY_FROM_TAURI_SIGNER_GENERATE placeholder).
Add two repository secrets on GitHub (Settings → Secrets and variables → Actions):
TAURI_SIGNING_PRIVATE_KEY — the contents of ~/.tauri/umux.keyTAURI_SIGNING_PRIVATE_KEY_PASSWORD — the password (empty is fine, set it to the empty string's secret)Until these exist, the release workflow fails at the signing step — that is intentional; an unsigned update bundle would be rejected by every client.
Local tauri build also needs the key (bundling now produces signed update artifacts). Export both variables in your shell before building locally:
export TAURI_SIGNING_PRIVATE_KEY="$(cat ~/.tauri/umux.key)"
export TAURI_SIGNING_PRIVATE_KEY_PASSWORD=""
npm run tauri dev needs no key — development never bundles.
With the secrets in place: publish release vX, then cut vX+1 as a draft/prerelease and install vX from it. The vX app should detect vX+1 on startup and from Settings, apply it in one click, and relaunch showing vX+1 (About/window title). Going offline and pressing Check for updates must show a clear offline message, not a crash.
umux is open source and contributions are welcome. To get started:
npm test
cd src-tauri && cargo test
Please keep changes within the project's scope (Linux/Windows/macOS desktop app, OSC-sequence-only completion detection; see the Roadmap in the spec for what is planned next). See the full product spec in plans/umux-prd.md for the design rationale and constraints.
See LICENSE.