Skip to main content
Commands, package names, and image names on this page come from the open-source project that Mibyan Desktop is built on, and can differ from the Mibyan Desktop installer. For the supported Mibyan install and update path, see Install and update.
The Python core runs fine from any git worktree — cd in and mibyan just works. The two TypeScript surfaces do not: ui-tui/ and apps/desktop/ each need a populated node_modules, and a fresh npm ci per worktree is slow and duplicates gigabytes across every branch you have checked out. htui and hgui are two shell helpers that close that gap. Each launches its surface from the current worktree while borrowing node_modules from one canonical checkout — so a throwaway branch costs a symlink, not an install. They’re developer conveniences, not shipped commands. Drop them in ~/.zshrc; adapt paths to taste.

The deps-sharing model

One checkout is the deps checkout — the one place you actually run npm install. Every other worktree links against it, and only re-installs locally when its lockfile diverges (a branch that bumps a dependency must not silently run against stale packages). Two env vars name the canonical checkout: Neither is read by Mibyan itself — they’re private to these helpers. The variables Mibyan does read are covered in Environment Variables.

htui — TUI from the worktree

The Ink TUI has a dev path already: mibyan --tui --dev runs the TypeScript sources via tsx instead of the prebuilt bundle. htui is a one-liner over it that also points the run at the current worktree’s ui-tui/:
--dev compiles from source, so it links ui-tui/node_modules from mibyan_MAIN_CHECKOUT when the root lockfile matches and installs locally otherwise (see _mibyan_root / linking helpers).
--dev and mibyan_TUI_DIR are mutually exclusivemibyan_TUI_DIR points Mibyan at a prebuilt bundle (Nix, system packages), which has no source to hot-reload. If it’s set in your shell, mibyan --tui --dev exits with an error. Run unset mibyan_TUI_DIR before htui.

hgui — desktop app from the worktree

The desktop app needs dependencies at both the repo root and apps/desktop/, a Vite server, and a Python backend. The stock npm run dev pins Vite to 5174; Electron also defaults to CDP port 9222 and takes a single-instance lock on its user-data directory. Changing only the Vite port is not enough to run two desktops. This zsh example gives each launch an explicit slot (HGUI_SLOT, default 0). Use a different slot in each terminal. It uses the shared helpers below and requires lsof:
For example, after setting mibyan_MAIN_CHECKOUT and sourcing the helpers:
Slot 0 uses ports 5174/9222; slot 1 uses 5175/9223. Slots are caller-assigned, not atomically reserved: always use distinct slots for simultaneous starts. Busy ports are rejected, never evicted. Use separate checkouts for separate builds because launches in the same checkout still share build outputs. Each slot starts with fresh desktop preferences and remembers them on later launches. This example does not copy browser storage, saved navigation, or backend ownership from a running app.
Separate desktops are not separate agent dataThe default mibyan_HOME is shared: sessions, configuration, credentials, and profiles remain the same. Avoid editing the same conversation from both instances. For destructive tests or incompatible database migrations, pass a separate temporary mibyan_HOME and configure that sandbox independently.
Quit the app normally or press Ctrl-C in its launching terminal. concurrently -k manages its own child commands, and Electron owns its backend shutdown. Do not add a global killport, pkill electron, or a sweep of all serve/dashboard --port 0 processes: those can terminate another instance. Remove the old _mibyan_gui_cleanup trap if replacing an earlier version of this helper.

Shared helpers

Both functions resolve the enclosing checkout and link deps the same way:
Why link only when locks matchA symlink to a divergent node_modules is worse than no install — the worktree would build against packages its own lockfile never declared. Byte-comparing package-lock.json is the cheap, exact guard: same lock ⇒ safe to borrow; different lock ⇒ npm ci locally. Vite realpaths symlinks before enforcing server.fs.allow, which is why apps/desktop/vite.config.ts whitelists the real node_modules location.

See also