Install

One command:

curl -fsSL https://dxcli.com/install.sh | bash

Or download a tarball for your platform and install manually:

tar xzf dx-0.8.3-linux-amd64.tar.gz
bash dx-0.8.3/install.sh

Available: linux-amd64 · darwin-arm64 · darwin-amd64 · windows-amd64

Then enable in any git repo:

cd my-project
dx enable

That's it. Make a commit and a session appears.

Commands

dx enable             # Install post-commit hook in current repo
dx disable            # Remove hook from current repo
dx init               # Create a .dx config file with defaults
dx snap [uuid]        # Save current Claude session (aliases: slap, yoink)
dx resume [-a] [id]   # Resume a saved session (-a = include other repos)
dx fork   [-a] [id]   # Fork a saved session into a new branch (alias: branch)
dx drop [uuid]        # Remove a snapped session from dx
dx ui                 # Open session viewer in browser
dx log                # Your snapped sessions (default)
dx log -t auto        # Per-commit auto sessions
dx log -t all         # Both
dx log -a             # Show all sessions
dx log -r             # Filter to current repo
dx log -n 50          # Show last 50 sessions
dx tidy --older-than 90d   # Prune old auto sessions (snaps never touched)
dx sync setup         # One-step cross-device setup (auto-detects gh CLI)
dx sync push [dst]    # Push sessions to a private GitHub repo, local path, or rsync remote
dx sync pull [src]    # Pull and merge sessions from the same
dx wake [-y] [n]      # Reopen sessions closed by a reboot or a closed tab (alias: restore)
dx color  [c]     # Pin a tab color to a session — dx wake reopens it colored
dx update [version]   # Update dx (e.g. `dx update 0.7.2` to pin, --force to reinstall)

Snap & Resume

Save your current interactive Claude session at any point:

dx snap
# => Snapped! Session d29c9dca-4f49-459a-9a01-7241213d3db0 (27 messages)
#      repo:   my-project
#      branch: main
#      commit: b56e1a6
#      resume: claude --resume d29c9dca-4f49-459a-9a01-7241213d3db0

Resume from any checkout of the same repo — even across different clones or from WSL when the session was created on Windows:

dx resume
# => Recent sessions for my-project:
#   * [1] 5m ago   b56e1a6  claude  main      d29c9dca...
#     [2] 1h ago   463be4a  claude  feature-x  463be4ab...
#
# => Use: dx resume <number> or dx resume <uuid>

dx resume 1
# => Resuming session d29c9dca-4f49-459a-9a01-7241213d3db0...

Running dx resume with no arguments opens an interactive picker with search, arrow-key navigation, and keyboard shortcuts:

  • Ctrl+A — toggle between current repo and all projects
  • Ctrl+T — cycle session types: your snaps → all types → autos
  • Ctrl+B — toggle branch badge and folder paths
  • Ctrl+R — rename the selected session (persisted across invocations)
  • Type to search by branch, commit, agent, folder, or custom name
  • Tab toggles between search and list focus

If the session was created on a commit ahead of your current checkout, dx warns you before resuming.

Snaps and auto sessions

dx records two kinds of sessions: snaps you save with dx snap, and auto sessions the post-commit hook records for every commit in an enabled repo. Snaps are the ones you manage day to day, so dx log and the resume picker show them by default — autos stay one Ctrl+T (or dx log -t auto) away.

Renaming an auto session in the picker, or coloring it with dx color, claims it: from then on it counts as one of your snaps. Searching in the picker never dead-ends — if nothing matches in the current view, the search quietly widens to all types.

Old auto sessions pile up over time. Prune them without touching a single snap (or any Claude transcript):

dx tidy --older-than 90d --dry-run   # preview
dx tidy --older-than 90d             # prompt, then prune
dx tidy --older-than 90d -y          # no prompt

Resume across repos

By default, dx resume and dx fork only surface sessions that belong to the current repo. Pass -a/--all to include sessions from every repo — handy when you want to continue a Claude conversation started in repo A while working inside repo B:

dx resume -a       # picker opens pre-toggled to all-projects
dx resume -a 3     # 3rd most recent across all sessions
dx fork -a         # same flag works for fork

Inside the picker, Ctrl+A still toggles between current-repo and all-projects views. dx warns when the session's commit isn't in the current checkout's history.

Aliases: dx slap and dx yoink do the same as dx snap. Aliases: dx hop and dx zap do the same as dx resume.

Wake after a reboot

Windows Update restarts your machine. Ten Claude sessions die with it. dx wake brings them all back — each in its own tab, in its own folder, resumed where you left off.

dx wake
# => Sessions you can reopen:
# =>    1. api-auth-refactor    ~/work/api        before reboot (2h)
# =>    2. viewer-ui-v3         ~/work/viewer     before reboot (2h)
# =>    3. changelog-pass       ~/work/dx         closed 14m ago
# => Reopen all 3 in new tabs? [Y/n]

Press Enter and three tabs open. It also catches the everyday case: a tab you closed by accident five minutes ago is right there in the list.

How dx knows

Claude Code registers each running session under ~/.claude/sessions/, but clears the entry when the session exits — so after a clean reboot there is nothing left to read. dx therefore keeps its own ledger: whenever it runs, it records which sessions are currently open. That happens on every commit (through the dx hook), on dx snap, dx resume, dx fork, and on every tick of the sync daemon.

The more dx runs, the fresher the list. Running dx sync enable keeps it under ~30 seconds old. You can also refresh it by hand:

dx wake --scan     # refresh the ledger, reopen nothing

Flags

dx wake              # list, then ask
dx wake -y           # reopen everything, no questions
dx wake 2            # reopen just number 2
dx wake --list       # show what would reopen, do nothing
dx wake --dry-run    # print the exact commands instead of spawning
dx wake --since 12h  # only sessions seen in the last 12h (default 72h)
dx wake --terminal print   # force an adapter (see below)

Terminals

dx opens tabs with whatever you are already using. It picks the first that applies, and you can override it with --terminal or the DX_TERMINAL environment variable.

AdapterUsed whenWhat happens
tmuxYou are inside tmuxOne new window per session
vscodeVS Code's integrated terminalPrints commands — VS Code cannot be scripted to open terminal tabs
wtwt.exe is available (Windows)All tabs open together in one Windows Terminal window
itermiTerm2 on macOSOne new tab per session
terminalTerminal.app on macOSOne new window per session
printAnything elsePrints ready-to-paste commands

Session colors

Pin a color to a session and dx wake reopens it colored — Windows Terminal tabs via --tabColor, iTerm2 via its tab-color escape sequence (Terminal.app has no tab colors):

dx color viewer-fixes tan       # palette name, or #rrggbb
dx color viewer-fixes --clear   # remove
dx color                        # palette + everything colored

Colors are stored by dx and sync across devices like session names. Colors set through the terminal's own UI can't be captured — set them through dx instead.

A session whose folder has been deleted is listed as [folder missing] and skipped rather than opened somewhere random. If claude turns out not to be on the PATH inside a new tab, the tab says so and stays open so you can read the message instead of watching it vanish.

What it does not do

dx wake restores sessions, not window layout. Your conversations come back; pane geometry, split positions, and scrollback do not. The ledger lives in ~/.dx/run/ and is never synced — project paths and session names stay on the machine that recorded them.

Session Viewer

dx ui

Opens a local web viewer showing all your sessions — both auto-created (from commits) and manually snapped. Click any session with a chat icon to see the full Claude conversation with messages, thinking blocks, and tool calls. Filter by repo, agent, or type.

Supported Agents

Agent Binary Session resume Status
Claude Code claude claude --resume (deterministic UUID, interactive picker) ✅ Tested
Gemini CLI gemini gemini --resume (search by commit slug) 🚧 Untested
Cursor Agent agent agent resume or agent --resume="<id>" 🚧 Untested
OpenCode opencode opencode run -s "<slug>" 🚧 Untested
OpenAI Codex codex codex resume [ID] 🚧 Untested
GitHub Copilot CLI gh copilot / copilot gh copilot --resume 🚧 Untested

By default, only claude is enabled. Other agents have hook scripts but are not yet tested. Configure via .dx file or DX_AGENTS env var. Missing binaries are silently skipped.

Configuration

.dx file

Run dx init to create one, or add it manually to your repo root:

# .dx
agents=claude gemini
# claude_args=--model opus
# gemini_args=
# cursor_args=--cloud
# opencode_args=
# codex_args=
# copilot_args=

Environment variable override

DX_AGENTS overrides the .dx file:

DX_AGENTS="gemini" git commit -m "test"

Per-agent args via env: DX_CLAUDE_ARGS, DX_GEMINI_ARGS, etc.

Quiet update check: dx and dx version print a one-line "dx X.Y.Z is available" nudge when a newer release is published — at most once every 7 days, silent on every failure (no curl, offline, etc.). Set DX_NO_UPDATE_CHECK=1 in your shell to disable.

Priority: DX_AGENTS env var > .dx file > default (claude).

Sync

Keep your snapped sessions in sync across machines — laptop, Mac mini, workstation. dx uses a private git repo you already own as the transport, so there's no server to run, no account to create, and no ongoing cost. Since it's git, sync is store-and-forward: push while the other device is offline, pull whenever it comes back online.

One-step setup (recommended)

On each device, run:

dx sync setup

If you have the gh CLI installed and logged in, dx auto-creates a private <you>/dx-sessions repo on the first device and offers to reuse it on the others:

# Device 1 (laptop)
$ dx sync setup
=> Detected gh CLI logged in as @alice
? Create private repo github.com/alice/dx-sessions for sync? [Y/n] Y
=> Created github.com/alice/dx-sessions (private)
=> Saved sync target to ~/.dx/config
=> Done.

# Device 2 (Mac mini), same GitHub account
$ dx sync setup
=> Detected gh CLI logged in as @alice
Found existing repo github.com/alice/dx-sessions
? Sync from it? [Y/n] Y
=> Fetching existing sessions...
=> Saved sync target to ~/.dx/config
=> Done.

From then on, on any device:

dx sync push    # upload local sessions
dx sync pull    # download remote sessions and merge

Without gh — any git URL

If you don't have gh, dx sync setup asks for a git URL. Any private git repo you can push to works — GitHub, GitLab, Bitbucket, Codeberg, Gitea, self-hosted. Paste either form:

https://github.com/you/dx-sessions.git
git@github.com:you/dx-sessions.git

Auth uses whatever you already have set up — SSH keys, credential helpers, Keychain, Windows Credential Manager, or a PAT. dx doesn't manage credentials itself.

Cross-device walkthrough

# On the laptop
dx snap                   # save the current Claude session
dx sync push              # upload it

# Later, on the Mac mini
dx sync pull              # fetch and merge
dx resume                 # picker shows the session — hit Enter to resume

Continuous sync (daemon)

dx sync push and dx sync pull are manual. For background sync, run a small daemon that watches your snaps and Claude session files and pushes on change:

dx sync enable      # start the daemon
dx sync status      # show what it's doing
dx sync disable     # stop it

Defaults: push debounced to 10s after the last write, pull every 30s. Override on start:

dx sync enable --push-debounce 5 --pull-interval 15

Aliases: dx watch, dx unwatch, dx watch-status, and dx auto-sync on|off|status all dispatch to the same handlers.

The daemon force-pushes a single squashed commit each cycle, so the sync repo stays at one commit — no history bloat. Conflicts with another device's push trigger an automatic fetch-merge-retry on the next tick. Logs go to ~/.dx/log/sync.log; the daemon is silent on stdout.

File watching uses inotifywait on Linux, fswatch on macOS, and a 2-second polling fallback on Git-Bash / MSYS and anywhere else missing the native tools. Install the native watcher for instant push:

apt install inotify-tools       # Linux
brew install fswatch            # macOS

dx enable also installs a pre-push hook alongside the existing post-commit hook. Both run dx sync trigger, which tells a running daemon to push+pull immediately instead of waiting for the next debounce window. So your latest commit and session always ride along with your next git push. The trigger is a silent no-op when the daemon isn't running, so the hooks cost nothing if you don't use auto-sync.

The daemon doesn't survive reboot. Add dx sync enable to your shell rc if you want it to auto-start across sessions.

Conflicts and merges

When two devices push independently, dx fetches and re-integrates remote changes before committing, then retries the push. Merges are non-destructive:

  • Session list — unioned by UUID; both devices' sessions are kept.
  • Names — remote wins on TSV conflict (last push wins).
  • Blobs — UUID-named, never collide; existing local blobs are never overwritten.
  • Dropsdx drop writes a tombstone that propagates to other devices on the next pull, removing the session there too.

Target resolution

dx picks the sync target in this order (first match wins):

  1. Explicit arg: dx sync push <target>
  2. DX_SYNC environment variable
  3. sync=<url> in ~/.dx/config (written by dx sync setup)
  4. sync=<path> in the current repo's .dx config

Alternative targets (no git)

If you'd rather not use git, dx sync push|pull still works with:

dx sync push /mnt/backup           # local path (USB, NAS, shared folder)
dx sync push user@host:~/dx-data   # rsync over SSH

You can also point DX_SYNC at a folder synced by iCloud, Dropbox, or Syncthing — those services handle the transport, dx handles the merge.

Security notes

The repo is private, TLS in transit, and encrypted at rest by your git host. dx doesn't add client-side encryption, so sessions are readable by anyone with repo access (including, in principle, the git host's staff). For most threat models this is fine; if you need stronger guarantees, host on your own server or wrap blobs in age or gpg before snapping.

How It Works

A git post-commit hook runs after every commit. It:

  1. Extracts the commit message and hash
  2. Generates a slug: fix-login-redirect-bug-a1b2c3d
  3. Launches each configured agent in the background with the slug as prompt
  4. Logs the session to ~/.dx/data/sessions.tsv

dx snap finds the most recently modified Claude session file in ~/.claude/projects/ and records it. dx ui reads the session data and Claude's JSONL conversation files, builds a self-contained HTML viewer, and opens it in your browser.

The hook never blocks git. It skips during rebase, merge, and other non-interactive operations.

Requirements

  • git and bash (always required)
  • node (required for dx ui only)

Platform Support

Platform Status
macOSSupported
LinuxSupported
Windows (Git Bash / MSYS2)Supported
WSLSupported

Uninstall

Remove from a repo:

dx disable

Remove globally:

rm -rf ~/.dx ~/.local/bin/dx

If you added the PATH export to ~/.bashrc, remove that line too.