Work on parallel features using coding agents.
Source and tooling for mx ("multiplexer") — a system for working on several features in parallel across multiple shared repos, using git worktrees. Each feature gets an isolated environment (its own worktrees, branches, ports, editor workspace), so you can switch between features instantly without stashing or branch-juggling.
This repo is the source for mx: a TypeScript pnpm monorepo with the core library, the CLI, the runtime templates, and the docs. The thing it produces — a single mx/ runtime with your cloned repos and live feature environments — lives wherever $MX_RUNTIME points (default ~/mx), fully decoupled from this repo.
Published on npm as @rousan/mx (command mx). For end-user install/usage, see npm/README.md. The npm/ folder is the publishable package — pnpm build populates it; npm publish ships it.
| what it is | where | |
|---|---|---|
| source | the core lib, CLI, templates, docs | this repo (github.com/rousan/mx) |
| runtime | clones + live feature environments | $MX_RUNTIME (default ~/mx) |
The runtime's CLAUDE.md is generated from templates/CLAUDE.md at the repo root (run pnpm build then mx sync to re-stamp). The per-work work.json and .code-workspace are owned and generated by the CLI — feature sessions read/mutate them only through mx commands. The runtime is versioned via <runtime>/mx.json (CLI major ⇄ runtime version); a mismatched runtime is gated until mx migrate. See CLAUDE.md for the source-of-truth and ownership rules.
The CLI resolves its location in order:
--runtime <path>flag$MX_RUNTIMEenvironment variable- default
~/mx
No pointer file is stored in the repo. Set export MX_RUNTIME=~/mx (or any path) in your shell, or rely on the default.
mx/ # pnpm workspace
├── package.json # root scripts: build / dev / mx / typecheck / lint / test
├── pnpm-workspace.yaml
├── tsconfig.base.json · eslint.config.js · .prettierrc.json · .nvmrc · LICENSE
├── CLAUDE.md # how to work on mx
├── README.md # this file
├── .github/workflows/ci.yml # typecheck/lint/test/build on PRs
├── scripts/release.sh # local release driver (pnpm release)
├── templates/ # runtime assets (CLAUDE.md, work.json, .code-workspace, context/INDEX.json, repo/{hydrate,health}.sh)
├── packages/
│ └── core/ # @mx/core — typed, tested domain logic
├── apps/
│ └── cli/ # @mx/cli (private) — CLI source
│ ├── src/ # tsup -> ../../npm/bin/mx.js
│ └── tsup.config.ts # bundles into npm/ and copies assets on success
└── npm/ # @rousan/mx — publishable package
├── package.json · README.md # committed (public metadata + consumer docs)
└── bin/, templates/, LICENSE # built by `pnpm build` (gitignored)
- Node >= 22 and pnpm (
corepack enableprovides the pinnedpackageManagerversion). - git on PATH.
- npm publish targets public
registry.npmjs.org; a corp.npmrcregistry is fine for installing deps (the firstpnpm installmay be slow on a corp mirror — it is not stuck).
git clone git@github.com:rousan/mx.git && cd mx
pnpm install
pnpm build # populates npm/ (bin/mx.js + templates/ + LICENSE)
export MX_RUNTIME="$PWD/.mx" # gitignored dev runtime in this repo
pnpm mx init # node npm/bin/mx.js init
pnpm mx repo add git@github.com:you/app.git
pnpm mx work new my-feature
pnpm mx infomx runs the built output, so re-run pnpm build (or keep pnpm dev watching) after changing CLI/core code. Templates live at /templates (no code, just content) and are copied into npm/templates/ at build — edits take effect on the next build. There is no global PATH coupling to this repo — the global mx comes only from installing a build (npm i -g @rousan/mx).
pnpm typecheck # tsc --noEmit across packages
pnpm lint # eslint (typescript-eslint + jsdoc + prettier)
pnpm test # vitest (unit tests for @mx/core)
pnpm build # tsup bundle -> npm/bin/mx.js + copy templates + LICENSE
pnpm dev # tsup --watch
pnpm mx -- <args> # run the local build (node npm/bin/mx.js)Domain logic lives in packages/core (@mx/core) as pure functions that return data and throw MxError; the CLI in apps/cli handles parsing, output, and exit codes. Add core logic with a Vitest test in packages/core/test. When testing, point MX_RUNTIME at a /tmp path (never a real runtime).
| command | does |
|---|---|
mx init [path] |
scaffold/adopt a runtime (repos/, works/, files/, .mx-root, mx.json, CLAUDE.md, context/INDEX.json) |
mx info [--all] [--porcelain] |
list repos, works, worktrees, and ports (active works only by default; --all includes archived) |
mx sync |
re-sync runtime with current mx version: re-stamp CLAUDE.md, backfill context/INDEX.json, the central hooks/ hub + runtime bin/ + runtime files/ store, each repo's repo.json, per-work dirs (wt//scripts//files//tmp//sessions/) + work CLAUDE.md. Same-major, non-destructive. |
mx update |
self-update the CLI within its major (npm i -g), then auto-run mx sync to refresh the runtime; reports a newer major if available. Not runtime-gated. |
mx migrate [--dry-run] |
upgrade an older-version runtime to the version this CLI supports (only command allowed on a version mismatch); --dry-run previews the plan without changing anything |
mx repo add <git-url> [--name <n>] |
clone a pristine repo into repos/<repo>/git (writes its repo.json) |
mx repo new <name> [--quick] [-o] |
create a fresh local repo (no remote): git init on main + README + initial commit. --quick also makes a dev-<name> work + a develop worktree (a one-shot for quick experiments) |
mx repo ls / mx repo -n <name> fetch|info|rm |
manage pristine repos |
mx repo health / mx repo -n <name> health |
local-only health check (on default branch? clean? in sync?), augmented by the central repo-health hook |
mx work new <name> [<repo>[:<branch>[:<base>]]]... [--branch <b>] [--base <ref>] [-o] |
create a work (prints its path); extra args are repos to make initial worktrees for (per repo: branch = :<branch> → --branch → work name; base = :<base> → --base → pristine HEAD); -o opens a fullscreen Terminal + starts the work's Claude session (macOS) |
mx work ls [--all|--archived] / mx work -n <name> info|describe|path |
manage works (ls shows active only by default; --all includes archived; --archived shows archived only) |
mx work -n <name> open (or -o) [--prompt <text>] |
fullscreen Terminal (macOS) that resumes or creates the per-work Claude session named <name>: 0 → create (seeded by the session-prompt hook / --prompt), 1 → resume, ≥2 → error (resume manually) |
mx work -n <name> worktree add <repo> [<wt-name>] / ls / rm <wt-name> |
manage worktrees; add fires pre/post-worktree-create, rm fires pre/post-worktree-remove. <wt-name> defaults to the repo — pass a distinct one for multiple worktrees of the same repo |
mx work -n <name> worktree set-branch <wt-name> [<branch>] |
after you git checkout a different branch in the worktree yourself, re-record its live branch in work.json (metadata-only — mx never checks out). Optional <branch> guards against a mismatch |
mx work -n <name> port set|unset|ls <wt-name> <service> [<port>] |
allocate/release ports per worktree (omit <port> to auto-pick a free one) |
mx work -n <name> archive [--yes|-y] |
remove worktrees; keep folder + work.json + sessions/ + branches (recoverable). Prompts for confirmation; --yes skips the prompt (required for --porcelain and non-TTY callers). Fires pre-work-archive (non-zero aborts: HOOK_FAILED) and post-work-archive (non-zero warns) |
mx work -n <name> unarchive [<wt-name>=<branch>...] |
re-create worktrees; positional overrides if recorded branches are missing. Fires pre-work-unarchive (non-zero aborts) and post-work-unarchive (non-zero warns) |
mx work -n <name> destroy --force |
permanent: delete the work folder (incl. sessions); branches kept. Prefer archive. |
mx work health / mx work -n <name> health |
local-only work-folder audit: stray files in the work root, worktree presence vs work.json, cross-work port collisions, archive invariants; augmented by the central work-health hook (--all includes archived) |
mx health [--all] |
whole-runtime overview: every repo's health + every active work's health in one view (--all includes archived works) |
mx mission-control [--port <n>] [-o] (alias mx mc) |
start a local, read-only live web dashboard — calm monochrome view of all repo/work health and a consolidated ports board (port → service → worktree → work → URL), streamed over SSE. -o opens the browser |
mx divider <text> [-o] |
fill a terminal with <text> as large block letters (a visual separator for macOS Spaces); bare takes over the current terminal and holds, -o opens a new fullscreen Terminal |
mx bin ls / mx bin path (alias mx bins) |
list the runtime's bin/ utility executables (mx-shipped + your own); path prints the dir for export PATH="$(mx bin path):$PATH" |
Read commands take --porcelain for stable JSON; mutations echo the resulting object; errors are {"error","code"} with a non-zero exit. -n <name> can be omitted when your cwd implies it (inside a work folder/worktree, or a pristine clone).
Releases are CI-driven: bump npm/package.json's "version" in a PR and merge to main — .github/workflows/release.yml runs the check pipeline, npm publishes from npm/ (auth via the NPM_TOKEN secret), tags vX.Y.Z, and creates a GitHub Release. Every merge to main that changes package content must bump the version or the run fails on the existing-tag guard. Merges touching only non-package paths — the landing site (apps/landing/**), docs/**, markdown, or repo tooling — are exempt via the workflow's paths-ignore, so a docs/site iteration ships to main (and redeploys the site) without an npm release. pnpm release (scripts/release.sh) remains a local fallback for emergencies. CI (.github/workflows/ci.yml) runs typecheck/lint/test/build on every PR.
See docs/release.md for the full runbook, the NPM_TOKEN setup, and every gotcha caught the hard way (npm name similarity, fresh-scope propagation lag, etc.).
-
mxCLI —init,status,sync,update,migrate,repo,work(worktree + port + path) - Runtime versioning (
mx.json+ version gate) andmx migrate; container repo layout (repos/<repo>/git+repo.json) - Central hook hub (
<runtime>/hooks/): worktree create/remove, work archive/unarchive, repo fetch, repo-health, work-health, session-prompt — any language, branch onMX_* - Health:
mx repo health,mx work health, andmx health(whole-runtime overview) -
mx mission-control— live web dashboard (zero-dep server + single-file React/Vite/Tailwind UI) -
mx divider <text> [-o]— fill a terminal with large block text as a visual separator for macOS Spaces - Multiple worktrees of one repo per work (named worktrees;
wt/<name>) -
mx work open/new -o— fullscreen Terminal (macOS) that resumes-or-creates the per-work Claude session, with asession-prompthook for the initial prompt - Env-based runtime discovery (
--runtime/$MX_RUNTIME/ default~/mx) - Per-service free-port allocation across all works (no fixed blocks)
- TypeScript pnpm monorepo (
@mx/core+apps/cli), lint/test/build tooling - npm distribution (
@rousan/mx) — CI-driven release on merge tomain, CI for PR checks - Optional: isolated per-env state (separate DB schema / container) for safe parallel runs