Desktop app for working on multiple coding branches with built-in terminals and Git workflows.
- Multi-branch workspace tabs (create, reopen, switch, close).
- Per-tab CLI terminal sessions (
codex,claude,kimi, oropencode) for branch work. - Custom CLI tools can be added and edited (name + command), then used per tab.
- Custom commands are validated on save, and missing commands show clear launch errors (local/SSH).
- User-selectable default CLI for newly opened projects and tabs.
- Bottom shell terminal (always plain terminal), resizable.
- Git sidebar with:
- unstaged/staged sections
- stage/unstage file or all
- discard file or all unstaged changes
- commit / amend / push / publish branch
- diff viewer for staged and unstaged changes
- Session persistence:
- last project path
- open tabs and active tab
- panel sizes and layout state
- branch tabs restored on app restart
- SSH remote project support (
ssh://user@host:/absolute/pathinternally).
- Electron + React + Vite
- Tailwind CSS
- Zustand
- xterm + node-pty
- Bun (recommended) or Node-compatible environment for tooling
gitavailable in PATHsshavailable in PATH (for remote projects)- Optional:
codex,claude,kimi, and/oropencodeinstalled where you want to run them- local tabs: local machine
- SSH tabs: remote host
bun install
bun run devBuild production assets:
bun run buildCreate distributable app (AppImage on Linux, DMG on macOS config):
bun run dist- Use
Localto open a local folder. - Use
SSHand enter:- host (example:
user@server) - absolute remote repo path (example:
/home/user/my-repo)
- host (example:
New Branchcreates a branch workspace tab.- Existing branches can be reopened from
Open existing branch.... - Branch workspaces are copied from the project root and skip
node_modules. - If the source repo has uncommitted changes, New Branch asks whether to keep or discard them in the copied workspace.
- New Branch includes an optional
Run bun install after branch setuptoggle (local and SSH).
- Main tab terminals run the selected CLI (
Codex/Claude/Kimi/OpenCode) per tab. - Default CLI is configurable under
CLI Settingsin the sidebar. - Custom tools added in
CLI Settingsappear in all CLI selectors. - Bottom terminal is always a shell terminal in the active tab folder.
- Drag the divider above the bottom terminal to resize split height.
- Stage/unstage files and view diffs.
- Commit or amend with message.
- Push or publish (
push -u origin <branch>). - Discard operations apply to unstaged changes.
- Remote project paths are stored as:
ssh://user@host:/absolute/path. - Git operations for SSH projects execute remotely over SSH.
- Branch copy/reopen/delete for SSH projects are also done remotely.
- If CLI commands fail in SSH tabs, verify the CLI exists on the remote host.
- Clipboard image paste in SSH tabs is forwarded by Duplex: the image is uploaded to
/tmp/codex-clipboard-*.pngon the remote host and that remote path is pasted into the terminal.
electron/
main/ Electron main process, IPC handlers, git/project/pty services
preload/ Renderer bridge API
src/
components/ UI panels (sidebar, terminal, git)
stores/ Zustand state
types/ Shared TS types
- Black window on Linux:
- Use the latest built app from this repo state.
- GPU acceleration is disabled for Linux in main process for stability.
- AppImage fails with
chrome-sandboxsetuid error or doesn't display:- Launch with:
./Duplex-*.AppImage --no-sandbox --ozone-platform=x11 - Or set environment variable:
ELECTRON_OZONE_PLATFORM_HINT=x11 ./Duplex-*.AppImage --no-sandbox
- Launch with:
- SSH connection fails:
- test directly:
ssh user@host - verify repo path exists and is a git repo.
- test directly:
- Push/publish fails:
- check remote auth and branch upstream configuration.