Changelog

Every dx release, newest first. dx update installs the latest; dx version shows the one you have.

0.12.2

Now on ARM Linux.

Added

  • dx installs on Linux on ARM (arm64): Docker and dev containers on Apple Silicon, ARM cloud servers and Raspberry Pis running a 64-bit OS. The documented install failed there before, because no ARM Linux build was made.

Changed

  • dx connect now runs rsntr 0.3.0. dx update moves the rsntr that dx installed to it: a checked download, then one restart of the node. A device set to LAN only stays on rsntr 0.2.1, because 0.3.0 can't do LAN-only between machines yet.
  • On a machine dx has no build for, the installer says so before it downloads anything, and names the builds there are. Windows on ARM installs the x64 build, which Windows 11 runs under emulation.

Fixed

  • On Windows, sending a session no longer stops the node for a moment (rsntr 0.3.0).
  • With rsntr 0.3.0, dx connect forget and dx share forget now also cancel the messages still waiting for that device or friend.
  • The folder that holds a device's connect key is now readable only by you on macOS and Linux.
  • After dx update, a running auto-sync restarts on the new dx; before, it kept running the old one until it was restarted.
  • dx connect join and dx receive try a knock again when it couldn't dial (a node that has just started), up to three times. When the other device won't take another knock from this one yet (it takes one a minute), they now say so ("Try again in a minute") instead of "could not reach".
  • On Windows, connect.log no longer has dx's lines overwritten by rsntr's own output. The serving rsntr now writes to ~/.dx/log/rsntr.log.
  • A message that a device made earlier but delivered later, after a newer one had arrived, was skipped for good. dx now reads messages in the order they arrive.
  • A session or message that arrived while dx was already receiving one no longer waits for the next message or an auto-sync tick. Windows with rsntr 0.3.0 hit this most.
  • dx sync disable stops auto-sync for real. A release's dx runs the script as a child, and disable stopped only that parent while the sync loop kept running. dx update stops a running daemon's loop everywhere, and on Linux, WSL and Windows also the loops an earlier dx sync disable left running. On macOS such a leftover loop keeps running until you stop it: ps -A -o pid,command | grep '[s]ync daemon', then kill <pid>.

0.12.1

Hooks where git runs them.

Fixed

  • dx enable puts the hook where git runs it. It works in a linked worktree or a submodule (both failed with "Not a directory"), and a repository's worktrees all share its hook. With core.hooksPath set, it no longer writes a hook git never runs: it names that folder, prints the two lines to add to its post-commit, and exits 1. dx disable cleans both folders. dx enable also makes the hooks folder when it is missing, and refuses a dx folder whose path holds a quote, $, a backtick or a line break.
  • A repo you removed dx from stays quiet. dx's hook line now does nothing once dx is gone. Before, a repo that dx was removed from without dx disable printed "No such file or directory" on every commit. A commit updates an older dx's line to the new form, and dx enable says when it updated one.
  • A session you dropped could come back when another device sent it with its id in other capitals, and a dropped session that someone shared back could be dropped again at the next sync. A drop now holds however the id is spelled.
  • On a file system that tells capitals apart, such as Linux's: dx drop, dx tidy and a drop from another device now remove a session's transcript copy however its id is spelled, a sync pull no longer brings a dropped session's copy back, and a session that comes from two devices keeps a single copy.
  • A name not yet marked, that dx 0.11 wrote with gawk 5.3 on another device (the awk Git for Windows ships), is recognized as dx's own: the session's title replaces it, as it does any name dx chose. A name that 0.12.0 already marked as a person's keeps that mark.

Changed

  • The Uninstall steps, in the README and on dxcli.com's docs page, say to run dx disable in each repo first, then remove the files, and what happens to a repo you miss.

0.12.0

Name a session; it sticks.

Added

  • dx sessions lists every session, newest first, with what dx knows about it. dx sessions --json prints all of it as one JSON document for scripts and tools, the way dx itself reads it: sessions, groups, devices and contacts; which sessions are open here and on your other devices, and which dx wake can reopen (when last seen, before the last reboot or not, whether its folder still exists); where each session's transcript is, how big it is and when it last changed; and where dx ui's page is and which sessions it lists. --here <folder> adds which sessions belong to that folder. --scan rescans first; without it the answer is as of the last scan, whose time the document gives. --transcripts also counts each transcript's messages and finds when it started; counts are kept, so a later run reads only what was added. --here, --scan and --transcripts work only with --json, and --json needs Node.js; the list for people does not.
  • Name a session from the command line. dx name <session> <name> (alias dx rename) gives a session a name, dx name <session> shows it, and --clear removes it here.
  • Open a session in a new terminal tab: dx open <session>. It opens where the session last ran, or in the folder --cwd names, and dx returns at once, so scripts can use it; --json says whether a tab opened, and gives the command to paste when none could. It uses dx wake's terminals (tmux, Windows Terminal, iTerm2, Terminal.app; in VS Code's terminal and anywhere else it prints the command), or the one --terminal names.
  • Exit codes a script can rely on: 0 ok (a no-op too), 1 error, 2 usage, 3 not found, 4 ambiguous, 5 needs a person, 6 cancelled.
  • --no-input (right after dx or after the verb) and DX_NO_INPUT=1: never ask. DX_INTERACTIVE=1 asks even when the terminal reports a pipe.
  • --json on the commands that change something: snap, drop, color, group, wake, tidy, send and receive print one object on stdout. It implies --no-input.
  • --help (or -h) on every command: its usage, exit 0, nothing else runs. dx help <command> shows the same.
  • dx ui --no-open builds the page without a browser, dx ui --out <path> writes it elsewhere, and dx ui --json gives page_path, built_at and sessions (the rows the page lists).
  • dx resume -y also answers the "commit outside this history" question. dx sync setup takes -y and a URL.
  • dxcli.com has a changelog page (dxcli.com/changelog.html, built from this file) and a privacy policy (dxcli.com/privacy.html), which every page links to.

Changed

  • A session target matches whole, never as a substring. dx resume, fork, drop, send, share, color and group take a session by its number in the list (resume and fork), its full uuid, a prefix of at least 4 of its hex characters, or its name — exactly, else ignoring the case of A–Z. They used to take the first session whose id merely contained what you typed, so dx drop 3 removed whichever session's id held a 3. A target that fits several sessions is now an error that lists them; a prefix shorter than 4 characters is read as a name.
  • A name sticks. A name you give with dx name or in the picker (Ctrl+R), and a /rename in Claude, is replaced only by a later /rename: never by a re-snap, by a /rename made before it, or by a device on an older dx. Names you gave in the picker before 0.12.0 stick too: the first snap after the upgrade keeps any name dx didn't make itself. When a re-snap keeps your name over a different /rename title, it says so and prints the dx name command that switches to the title. A name made only of hex digits and dashes is refused, so it can't be mistaken for a session id.
  • dx finds a folder's Claude sessions by Claude's own rule. Claude names a project folder by turning every character but letters and digits into "-" (and shortens a very long name with a hash); dx turned only ":", "/", "\" and "_". Any folder with a dot, a space or another character in its path — every .claude/worktrees/* checkout — got the wrong name: a plain dx snap there found nothing, and dx resume linked transcripts into a folder Claude never reads.
  • Resume and fork link the newest copy of a transcript into a folder that has none: the newest under any Claude project folder, else the copy dx keeps from another device — not whichever one a directory listing returned first. A copy already in the folder is still never replaced.
  • dx resume, dx fork and dx open follow a Claude projects folder that is a link. A transcript copy that dx keeps as a link is not followed (dx keeps them as regular files).
  • Edits from a device that has the session open apply here. A rename, color or group change made on another device to a session that is open there is meant to take the session over; the receiving side never read that the session was open, and silently refused it.
  • One row per session. Every write to sessions.tsv and its sidecar files keeps a single row per session, and a rewrite folds away duplicates an older device or a hand edit left behind. A session keeps its newest row, and stays snapped when any of its rows was a snap — a sync or another device can no longer turn it back into an auto. In the sidecar files the last row wins, as it already did for the picker and the viewer. A commit hook that runs several agents records one session, under the first of them.
  • Usage errors exit 2, not found 3 and ambiguous 4; previously they exited 1. An unknown sub-command of dx sync, dx connect or dx _admin printed its usage and exited 0; it now exits 2 with the usage on stderr. Cancelled (n at any prompt, Esc in the picker) is exit 6, previously 0 everywhere. A question with nobody to answer is exit 5; previously it was 1 at snap, resume, fork and sync setup, and 0 at tidy, wake and share.
  • dx asks nothing without a terminal: it says which flag answers instead. The picker needs a console.
  • dx help, -h and --help change nothing. An unknown command prints the help to stderr and exits 2; an unknown argument to dx ui (it was silently ignored), or an unknown flag or extra argument to dx sync setup, is a usage error (exit 2) too.
  • dx log shows one line per session. A session that a person named on another device is now in the default list, because it already counts as claimed (the resume picker always listed it). -t auto and -t live filter by the session's type. Rows with a broken id or time are hidden. --uuids prints ids in lowercase, as dx sessions --json does, and --group and --device ignore the case of A–Z only, as finding a session by name does.
  • dx color --group colours the sessions the group lists, including a session named on another device. On a group whose sessions are all unclaimed it says so, and how to list them (exit 3).
  • dx tidy never removes a session whose newest row it does not recognize, and decides per session: an older snap row keeps a session.
  • dx connect list counts sessions, not rows. dx connect now sends the transcript of a session whose id is stored in capitals (it sent only the session's details before), and sends a session spelled two ways once.
  • dx sync bundles transcripts from one listing of the whole store, keeps each copy's modification time, and no longer makes a second copy of a transcript whose id is spelled differently.
  • A session's /rename title, first message and folder are read in full: a quote in a title no longer cuts it short.
  • Names, group labels and the open sessions a device reports are stored without control characters, and cut by character instead of by byte. Names and group labels are also stored as valid UTF-8: a broken byte sequence is dropped. After the update, a device may receive once more the details of sessions whose name or row holds non-ASCII characters.
  • A session's name in a message never carries a control character, even a name an older version stored.
  • Messages name a session by the first 8 characters of its id, as dx name does. The full id stays in commands and in --json. The help and every command's usage fit an 80-column terminal.
  • dx fork says what a fork is: the copy remembers everything so far, then goes its own way. It never made a git branch, and its help no longer says so.
  • dx ui opens its page through dx's shared page opener: from WSL that is the Windows browser, and when no opener works dx says where the page is instead of failing. The page is written private to its owner (0600).
  • dx ui's count is "Snapped or named": the sessions you snapped or gave a name, which dx log lists by default.
  • dx init writes a .dx for Claude Code only, and notes that hook scripts for other tools exist but are untested and unsupported.
  • dx update's "what's new" link opens this page on dxcli.com. It pointed at a release page on GitHub that most people could not open.
  • The copyright line in dx version and the LICENSE name Axiom DX Labs, LLC.

Fixed

  • dx connect and dx wake work with the awk Debian 12 and Ubuntu 22.04 install by default. That awk (mawk 1.3.4) has no {n} repeat count in a pattern, so dx never recognized a device, a transcript, a session open on another device or one to reopen: connect sent nothing and wake found nothing. A second mawk quirk would then have made every push send each session's details again; that is fixed too.
  • dx runs with busybox awk (Alpine and other small systems). busybox read a few lines of dx as calls to functions that do not exist ("Call to undefined function"): the resume and fork lists and device sync failed, and dx connect pair, join and dx feedback printed the error.
  • A name, group label or folder path whose bytes are not UTF-8 no longer breaks dx on a Mac. macOS's awk stops at such bytes when it matches a pattern, so dx log came out empty and device sync stopped; gawk cut the row short. dx log columns now line up on every system.
  • dx feedback works in the bash every Mac ships (3.2). It stopped with "unbound variable" before sending, so a note could not be sent from a stock Mac, and a title with an accent, a dash or the ellipsis of a long first line reached the maintainer as bytes that are not UTF-8.
  • A device you pair or join gets your sessions right away, even while another send is running. dx skipped its own send when one was already going, and the one going had not heard of the new device, so the sessions waited for some later push. Pairing now waits for it (and says so after two seconds), and tells you when the sessions have to follow later; joining no longer says "Sent N sessions" when it sent nothing.
  • A device whose sync hook had been switched off, or whose hook setup failed, could silently stop receiving from your other devices. dx now turns the hook back on, checks that it took, and says so if it can't.
  • dx ui no longer leaves out the chat of a session whose folder has a dot or a space in its path.
  • dx reads a store file saved with Windows line endings or a BOM.
  • dx tidy no longer stops on a commit with a regex character in it.
  • dx wake on macOS read the wrong number for the last boot.

Notes

  • dx sessions --json and the --json results are dx's own JSON: dx's own tools read them, and they may change until 0.13.
  • docs/CONTRACT.md describes dx from a script: the dx sessions --json document, the exit codes, the --json results and the rule that dx never asks a question without a terminal.

0.11.13

Fewer places to trust.

Changed

  • dx connect install checks rsntr against a hash pinned inside dx. It used to fetch the expected hash from the same GitHub release as the download, so anyone who could replace the release's files could replace both. The hashes of the five rsntr 0.2.1 files are now in dx itself, covered by the signed dx release, and a file that doesn't match is refused before anything is unpacked. DX_RSNTR_SHA256 names a different hash to expect, for a mirror that holds another build.
  • dxcli.com and dx ui no longer load fonts from Google. Inter and JetBrains Mono (SIL Open Font License) are served from dxcli.com's own files, and the dx ui page embeds them, so neither page asks any other host for anything, and Google no longer sees your IP address when you open them. The pages look the same.
  • The key fingerprints are published on a second site. The README, the docs page and SECURITY.md point to axiomdxlab.com/security, so a first install can be checked against something other than dxcli.com.

0.11.12

Releases are signed, and the installer checks them.

Added

  • The installer checks what it downloads. install.sh fetches SHA256SUMS from the same site and stops, before it unpacks anything, if the tarball doesn't match its line in the list, so a corrupt or truncated download can't install. DX_SKIP_VERIFY=1 skips the check, for a mirror that doesn't carry the list.
  • Releases are signed. SHA256SUMS has an OpenSSH signature, SHA256SUMS.sig, from one of two keys listed in release-keys, which now ships in every tarball. An install keeps those keys as ~/.dx/release-keys. From then on the installer and dx update take a release only if its list carries a good signature from one of them, so a taken-over dxcli.com can't get a swapped installer or tarball past an install that has the keys. dx update also checks the installer against the signed list before it runs it.
  • Verify a release by hand. The README and the docs page ("Verify a release") give both keys, their fingerprints and the ssh-keygen -Y verify commands. The very first install still trusts dxcli.com once, because there are no keys to check against yet; verify the release yourself to avoid that.

Changed

  • Once keys are pinned, dx update and the installer need ssh-keygen (OpenSSH 8.0 or newer). Git for Windows, macOS and current Linux have it. Without it dx update says so and stops; DX_SKIP_VERIFY=1 goes ahead anyway.

0.11.11

Fork any session by name, snapped or not.

Changed

  • dx fork <name> and dx resume <name> find a session you never snapped. They only looked among your snaps, so dx fork dx-soc2 said "no session matching" for a Claude session that dx snap dx-soc2 found a second later. When no snap matches, they now look through Claude's sessions the way dx snap <name> does (this folder first, then every folder), show what they found, and ask Snap it and fork it? [Y/n]. Enter snaps it and carries on; -y skips the question; with no terminal to ask, dx prints the dx fork -y … command instead. A session from another folder is recorded as that folder's, the same as dx snap records it, and a name shared by sessions in different folders is listed for you to pick by uuid, never guessed.
  • A session you snapped and later renamed in Claude is found by its new name, without asking.
  • Your first dx resume <name> or dx fork <name> works before you have snapped anything.

Fixed

  • Sessions the commit hook starts no longer inherit git's hook environment. Git runs hooks with GIT_DIR, GIT_INDEX_FILE and the commit's author and date set, and an agent session started from the hook kept them: its git commands acted on the committing repo whatever folder it was in, and its own commits carried that commit's author and date.
  • dx update links the release notes at their new home, github.com/Axiom-DX-Labs/dx.

0.11.10

A note to the maintainer, in one command.

Added

  • dx feedback <note> — tell the maintainer something in one command: no account, nothing to install, no form. dx bug <note> and dx idea <note> say which kind it is; bare dx feedback asks for one line; dx feedback < notes.md sends a longer note. Along with the note go the dx version, OS, bash and Claude Code versions and the terminal — the line printed before sending is exactly what is attached; never paths, names or sessions. --bare sends the note alone, --dry-run shows the whole issue and sends nothing.
  • It always lands somewhere. The note goes to a small relay (feedback.dxcli.com) that files it as an issue in the dx repo. If the relay cannot be reached it waits in ~/.dx/feedback/outbox/ and a detached worker retries for a few minutes. The next bare dx says when a note went through and gives waiting ones another try; dx feedback --retry sends them on demand.
  • Limits: a note can be up to 16 KiB, and one address can send six notes per ten minutes. Nothing is sent unless you run dx feedback.

0.11.9

Call a group what you call it.

Added

  • dx project, dx tenant, dx herd and dx bucket are aliases for dx group, and so are the plurals (dx groups, dx projects, dx tenants). Bare, they list the groups.
  • The flags take the same words. In dx log, --project and --tenant work as --group, --projects and --tenants as --groups, and --by-project and --by-tenant as -g. dx color takes --project and --tenant for --group. A missing value now names the flag you typed.

0.11.8

A maintenance release.

Unchanged

  • Every command works as it did in 0.11.7: internal changes only.

0.11.7

The session viewer knows your session names.

Changed

  • dx ui shows and finds sessions by name. Every row and chat view was titled by its git branch, and the search box never looked at names, so a session you knew by its name (the one dx log and the picker show) could not be found by it. A named session is now titled by its name with the branch beside it, and typing part of the name finds it. Unnamed sessions look as before.

0.11.6

The session viewer loads every history again.

  • Fixed: dx ui came up blank. The viewer inlines your sessions into a script block, and a browser ends a script at the first "</script" it meets — even inside a transcript that quotes HTML. Such a history showed an empty list, a syntax error in the console, and fragments of transcripts spilled across the page. The data now goes out with every "<" escaped, which reads back as the identical text.

0.11.5

A quieter install and update.

Changed

  • The install is a handful of lines. It says what it is downloading, where it put it, and ends with one line: ready. run 'dx' to get started. The detail that used to scroll past (platform detection, a temp "Source" folder, "Files copied", "Directory ready") is behind DX_VERBOSE=1. If ~/.local/bin is not on your PATH or Node is missing, the warnings come before the last line and it says "almost there" instead of "Done!", because dx would not run yet. The PATH hint names the right profile file for your shell.
  • dx update says what it is doing, then the result, in the style of herdr update:

    checking for updates...
    already up to date (0.11.5)

    An update prints updating 0.11.4 to 0.11.5... and updated. what's new: <link>. The installer no longer repeats its own ceremony inside an update, and no longer tells someone who just ran dx update to run dx update anytime. dx update --verbose shows the installer's detail.

  • Version numbers in these messages drop the leading v, to match dx version.

Fixed

  • dx update 0.0.1, a version that does not exist, printed a wall of gzip and tar errors. It now says that version is not available for your platform. An installer that cannot be reached says could not reach dxcli.com. dx 0.11.4 is unchanged.

Notes

  • Anything that parses the old ==> lines of the installer should use DX_VERBOSE=1 for the detail, or dx version for the version.

0.11.4

Windows receives again, and sends faster.

Fixed

  • Windows stopped applying what its other devices sent, from 0.10.1 until now. The receive that the node's hook starts in the background was run through bash whenever dx had no .exe. An install can hold the compiled program under both names, bash refuses to run it, and the receive died the instant it started. So did the share-knock worker. Other devices showed as "seen days ago", their sessions and presence never arrived, and a new friend's dx share only worked while its terminal stayed open. dx now decides by what the file is, not what it is called. If your Windows device is behind, run dx update, then dx connect _recv once to apply what is waiting.
  • One attachment that cannot be fetched no longer freezes everything behind it. Receive stopped at such a message and every later one, presence and renames included, waited for as long as the sender stayed unreachable. It is still held back, but only for three passes over ten minutes. Then it is dropped, the rest flows, and the sender is asked, at most once an hour per session, for a fresh full copy. A dropped copy is never applied late over something newer.

Faster

  • Sending to several devices on Windows pauses the node once, not once per device. Importing an attachment there means stopping the node and starting it again, about 9 seconds per send. A push now does that once, and again only after every 20 sends so a big backlog never keeps the node down for long. Ctrl-C brings the node back first. Each send also skips a helper start it did not need.
  • A device not heard from for a day gets only the small record. Its transcripts used to pile up in an outbox that expires, and each one cost a node pause. The full copy follows once the device is heard from again. dx send <session> <device> always sends everything. DX_CONNECT_OFFLINE_AFTER sets the limit in seconds and 0 turns it off.
  • dx connect join and dx connect pair show what they are doing. They used to go silent while sending your sessions to the new device. They now say how many and show a counter.

Notes

  • DX_CONNECT_DEFER_TRIES and DX_CONNECT_DEFER_SECS change those limits; the bookkeeping lives in ~/.dx/connect/deferred.tsv.

0.11.3

Resume opens fast on Windows.

  • dx resume, dx fork and dx drop open their picker in about a second on Windows (measured 27s → 0.9s on a 296-session store). Deciding which sessions belong to the current folder ran as a bash loop that started several processes per session — about 50ms each on Git Bash — and the same loop existed in five places. It is now one awk pass applying the same four matching rules, and the open-sessions refresh no longer blocks the picker on a terminal (scripts still get a settled ledger).
  • Fixed: a " or \ in a branch name made the picker fail to start, and rows missing a session id showed up as blank picker entries.

0.11.2

Snap a session from any folder.

  • dx snap <name> works from any folder. If no session in the current folder matches the name or uuid, dx looks through every folder Claude has sessions in, shows where it found one, and snaps it after a yes — Enter accepts, -y skips the question, and with no terminal to ask dx won't snap it and prints the dx snap -y … command instead (a snap also goes to your paired devices). The snap records that folder's repo, branch and commit, as if you had run it there; a session whose folder was deleted is recorded by path, and one started in a subfolder of the current repo needs no confirmation.
  • dx never guesses between folders: the same name on different sessions in different folders is listed for you to pick by uuid. A session dx resume linked into several folders counts once, as its newest copy.
  • Fixed: a snap target found in the folder's path snapped an arbitrary session. The uuid-fragment match ran over each transcript's full path, so dx snap dx inside ~/…/dx (or dx snap .) matched every session and silently snapped whichever sorted first. Fragments now match session ids only, literally; an id fragment several sessions share is an error listing them, unless one session's exact name settles it.

0.11.1

One color for a whole project.

  • dx color --group <group> <color> colors every session in a group at once. Each member gets its own color, so dx wake, replication to your devices, dx share and .dxsession bundles behave exactly as for a session colored by hand. --clear clears them all; dx color --group <group> lists the members and their colors. Membership is what dx log -g shows (snaps and claimed autos); a session added to the group later is not recolored until you run it again.
  • Plain dx color <name> <color> also accepts a group name when no session matches it — a session always wins a name clash, and --group forces the group. A miss on both now says no session or group matching.
  • dx log --uuids prints just the uuids of the listed sessions, one per line, honoring every filter — for scripting.

0.11.0

Sessions by project.

  • dx group <session> <group> — put a session in a project group. Every session already belongs to one: by default the repo it was snapped in (the same name dx log shows under REPO), so twenty sessions fall into six projects with no setup; a label moves a session elsewhere — two repos into one project, or one repo split into several. dx group <session> shows its group, --clear returns it to its repo's group, and bare dx group lists every group with counts.
  • dx log -g groups the listing under one header per group, newest activity first; --group <g> shows one group (case-insensitive); --groups prints one line per group with its session count and last activity. All combine with -t, -r and -n.
  • Wherever names and colors go, groups go too. groups.tsv merges with dx sync, replicates to your devices over dx connect, rides along with dx send, dx share and .dxsession bundles, and dx drop / dx tidy clean it up. The resume picker shows a #group badge on labelled sessions and searches by group. Grouping an auto session claims it, like naming or coloring it.

0.10.1

Share works from Windows.

Fixed

  • dx share and whole-session sends from a Windows machine never left it. rsntr 0.2.1 has no control socket on Windows, so sending a transcript as an attachment (chat send --file) found the blob store locked by dx's own serving node and failed. dx share still said "sending…" and waited ten minutes for an acknowledgement that could not come, and the same failure silently kept whole sessions from replicating from Windows to your other devices. dx now pauses its node for the import (a couple of seconds offline; peers retry on their own) and starts it again — one pause per attachment.
  • A failed send is reported. dx share prints ✗ could not send to <friend>: <reason> and the retry command — the friend is a contact by then, so no new code is needed. dx share <session> <friend> exits 1 with the reason. rsntr's error text is on its stdout, which dx used to discard; connect.log now keeps it.
  • dx connect status on Windows reported the node as not running while it was.
  • A friend who received from you before can redeem a new share string. Their node is admitted by yours, so a second knock is answered on the spot and never reached the owner: dx receive waited five minutes and dx share ten, with nothing in between. The code now travels as a message when the knock comes back pre-admitted, and the sharer redeems it the same way.
  • A transcript from a peer that restarted meanwhile now arrives. When the sender's node came back on new ports (a Windows sender does that for every attachment; LAN-only mode has no relay to find it again), the receiver kept dialing the old address, and the hello that carried the new one waited behind the very message it was needed for. A blob that cannot be fetched now looks ahead for the peer's newer ticket and tries once more before deferring.
  • Windows: a brand-new device received nothing until its node was restarted. Hooks registered after the node started were not seen by it; a fresh node now gets its hooks before it starts.
  • Windows: a dropped session's transcript that another process had open at that moment is now deleted after a short retry rather than left behind.

Notes

  • Windows: a share knock that arrives through the node's hook is handled by a detached dx connect _knocks worker, since pausing the node cannot happen inside its own hook. docs/rsntr-feedback.md describes what an rsntr-side fix would look like.

0.10.0

A session for a friend.

Added

  • dx share <session> — hand one session to someone else who has dx. It prints a share string (good for 24 hours, once); your friend runs dx receive <string> and gets the session with its transcript, then dx resume continues it as their own fork: the received transcript stays as received, their continuation gets its own id, and sharing back never collides. Before anything leaves, dx shows what goes (name, size, messages, repo) and asks — a transcript can quote file contents and secrets.
  • Contacts. The first share pairs you as contacts; later shares are dx share <session> alice, no code. A contact is never a device: no introductions, no presence, no replication, and nothing they send but a share is ever applied here. dx share list, dx share forget <who>.
  • dx share <session> --file writes a .dxsession bundle (row, name, color, transcript) for email, chat or a USB stick; dx receive <file> imports it. No node needed on either side.
  • A received session is yours: it replicates to your own devices, dx log and the picker show who shared it, and the prompt mentions new arrivals once.
  • Share is for people, not for your own devices — those already get every session. dx receive refuses a string from one of your paired devices on the spot and points at dx send, instead of waiting in silence and leaving a stray contact behind; dx share says the same on screen if such a device knocks.
  • Fixed: dx share on Linux and WSL printed a stat filesystem report and an integer expression expected error before the confirmation prompt. Finding the newest transcript tried BSD stat -f first, which on GNU systems means filesystem-stat; it now uses stat -c first and falls back to BSD on macOS.

Notes

  • Wire: a share message from a contact carries the row and the transcript as a verified attachment; the receiver answers share-ack, which is what dx share waits for. Share codes are single use with a 24-hour life; the node hook handles the knock, so dx share does not have to stay open.

0.9.0

Your devices, connected.

Added

  • dx connect — pair your own devices into a network; every snap, rename, color and drop then replicates to all of them, peer to peer, with no repo or account in the middle. The transport is rsntr: an encrypted QUIC node per device (iroh), which dx connect install downloads for your platform, sha256-verified, into ~/.dx/bin. Pairing is one paste: dx connect pair on a device you already have prints a string, dx connect join <string> on the new one — and every device already in the network is introduced to it automatically. dx connect lists devices (online/offline, last seen, sessions, what is open where); name, forget, status and stop do what they say.
  • Live mirroring. Sessions open on a device are mirrored to the others as they grow — whole lines only, small tails inline, big ones as verified attachments — and show as type live until they are snapped. dx resume of a session from another device works like a synced one, and the device where a session is open owns it while it is open.
  • dx send <session> [device] — push one session now, to one device or to all; --full resends from scratch.
  • Presence. dx connect shows which sessions are open on each device right now.
  • Device-aware surfaces. dx log gains a DEVICE column once a session from another device exists, plus --device <name> and -t live; the resume picker shows an @device badge and searches by it; dx ui gets a Device filter and reads synced transcripts from blobs/, so a session from another device opens in the viewer.
  • The sync daemon carries peers. dx sync enable now runs on paired devices alone — a git target stays optional; dx sync status counts them. Each push cycle sends what changed, each pull tick receives and reports presence. Without the daemon, dx snap, dx drop, dx color and dx send push on the spot, and the rsntr message hook receives the instant something lands, with no dx process running.

Notes

  • rsntr is at 0.2.1 and can only serve detached on unix: Windows (Git Bash) is best-effort in this release — dx runs the node as a foreground child and polls for messages on daemon ticks.
  • ~/.dx/node holds this device's private key: never copy it to another machine, never put it in a synced folder (dx refuses iCloud/Dropbox/OneDrive paths). It is not part of dx sync.
  • ~/.dx/data/origin.tsv (which device a session came from) is a uuid sidecar like names.tsv, so sessions.tsv keeps its 8 columns. rsntr 0.2.1 has no blob garbage collection: the sending node's store keeps every full session copy it ever sent.
  • The design, the wire format and the ownership rule are in docs/CONNECT.md.

0.8.4

Snap by name.

  • dx snap <name> snaps a session by the name you gave it with /rename — the same name Claude prints in claude --resume "<name>" when a session exits. A uuid (or any fragment of one) still matches first; then names, case-insensitively: an exact name wins (the newest session if several share it), a partial name is accepted only when it matches a single session, and an ambiguous partial name lists the candidates instead of guessing.
  • A target that matches nothing now says so explicitly: no session matching '<target>' found (by uuid or name).

0.8.3

Snap-first views.

  • dx log shows your own snaps by default. The per-commit auto sessions the hook records are behind -t auto; -t all shows everything. New TYPE and NAME columns, and REPO/BRANCH now mean what they say: auto sessions record the real branch at commit time, non-git sessions show <path> (folder) with - for commit and branch, and old auto rows that stored the commit slug in the branch field display it under NAME instead.
  • The resume picker opens on your snaps too. Ctrl+T cycles snaps → all types → autos; the footer counts what's hidden. Naming an auto session (Ctrl+R in the picker) or coloring it (dx color) claims it — it then counts as one of yours. A search that matches nothing in the current lens quietly widens to all types, so filtering never dead-ends.
  • dx tidy --older-than 90d prunes old auto sessions — rows, names, colors, and synced blobs, each tombstoned so a later sync can't resurrect them. Snaps and claimed autos are never candidates, and Claude's transcripts under ~/.claude are never touched. --dry-run previews, -y skips the prompt.
  • Auto sessions now keep their commit slug as the session name and record the real branch in the branch column.

0.8.2.1

Non-git snap fixed on Windows.

  • Fixed: session commands in a non-git folder on Windows. On Git Bash, dx snap (and resume, fork, drop, log -r) in a folder that isn't a git repo always failed with no Claude sessions found for this repo. — dx keyed the folder by its MSYS path (/c/Users/...) while Claude Code, a Windows process, keys its project dir by the Windows path (C:/Users/...). The folder identity now uses the Windows form. macOS, Linux and WSL are unchanged.
  • Fixed: silent exit when a snap target matches nothing. dx snap <target> with a target that matches no session exited with no output at all; it now prints no session matching '<target>' found.

0.8.2

Session colors.

  • dx color <session> <color> — pin a tab color to a session (a palette name like blue/tan, or #rrggbb); dx color <session> shows it, --clear removes it, bare dx color lists the palette and every colored session. Colors live in ~/.dx/data/colors.tsv (uuid → hex) and sync across devices with the same union merge as session names; dx drop cleans them up.
  • dx wake reopens colored sessions colored. Windows Terminal tabs get --tabColor; iTerm2 tabs get the color via iTerm's OSC 6 sequence from inside the tab. Terminal.app has no tab colors. Colors you set through the terminal's own UI are not captured — pin them through dx and they survive wakes, reboots, and machines.
  • Only a strictly validated lowercase #rrggbb is ever stored, and it is re-validated at every point of use before reaching an argv or an escape sequence.

0.8.1.2

Build provenance in the banner.

  • Packaged builds stamp the short commit hash into dx version — dx 0.8.1.2 (wake) [abc1234] — so a bug report pins the exact commit an install was built from. Display-only: nothing parses it, and the version machinery is unaffected. Dev checkouts show no ref (honest: they aren't a published build); a "+" suffix marks a build from a dirty tree.

0.8.1.1

Four-segment versions.

  • Versions may now carry an optional fourth segment (X.Y.Z.W) for small same-patch respins. Unlike the retired "build N" suffix — which dx update could not see, so rebuilds needed --force — the fourth segment is part of the version proper: dx update treats 0.8.1 -> 0.8.1.1 as a normal forward update, and the weekly update nudge announces it. No parser changes were needed: the version machinery (update, nudge, installer, build script) already accepted dotted tails; this release just starts using them. The version string drops the build suffix: dx 0.8.1.1 (wake).
  • dx update now says how to reinstall the same version. The "already up to date" no-op mentions dx update --force, so the escape hatch is discoverable at the moment it's needed.
  • Supersedes 0.8.1 build 2 and carries its wake-tab transcript relink.

0.8.1 build 2

Wake relinks transcripts before resuming.

  • dx wake tabs no longer fail with "No conversation found" for sessions whose recorded folder differs from their launch folder. Claude scopes --resume <id> to the current folder's transcripts, but a session started in folder A that moved into a worktree (e.g. a Claude-harness worktree under .claude/worktrees/) records the worktree as its cwd — so the transcript lives under A's project dir and the resume died in the freshly opened tab. _wake-tab now relinks the transcript into the recorded folder's project dir before launching claude — the same find-and-copy dx resume has always done — covering moved sessions, harness worktrees, and synced-in sessions alike. Found live on macOS during the 0.8.1 release verification; the hold-open-on-failure tab is what made it visible.

0.8.1

Your sessions survive the reboot.

Added

  • dx wake — reopen the Claude Code sessions that were open before a reboot, a crash, or an accidentally closed tab. Each one comes back in its own terminal tab, in its own folder, resumed where you left off. Alias: dx restore.
  • Session ledger — dx now remembers which sessions are open while it runs. It reads Claude Code's process registry (~/.claude/sessions/) and records the live, interactive ones to ~/.dx/run/live-sessions.tsv. The scan is cheap and silent, and happens on every commit (via the dx hook), on bare dx, on dx snap / dx resume / dx fork, on each sync-daemon tick, and on demand with dx wake --scan.
  • Terminal adapters — dx wake opens tabs using whatever you're in: tmux, Windows Terminal (all tabs in one atomic launch), iTerm2, or macOS Terminal. Inside VS Code — which can't be scripted to open terminal tabs — and anywhere else, it prints ready-to-paste commands instead. Force one with --terminal <t> or DX_TERMINAL.
  • Wake flags — -y/--yes to reopen everything without asking, --list to look without touching, --dry-run to see the exact commands, --since <dur> to widen or narrow the window (default 72h), dx wake <n> to reopen just one.
  • Post-reboot hint — once per boot, dx mentions how many sessions are waiting to be reopened.

Changed

  • The sync daemon refreshes the session ledger on each pull tick, so with dx sync enable running the list is never more than ~30s stale.

Notes

  • dx wake reopens sessions, not window layout — tabs and their conversations come back; pane geometry and scrollback do not.
  • The ledger lives under ~/.dx/run/ and is never synced — your project paths and session names stay on the machine that recorded them.

0.8.0 build 2

Snap anywhere, plus a critical installer fix.

  • install.sh no longer silently aborts on macOS/Linux. detect_platform's last line was a bare [ "$os" = "windows" ] && BIN_NAME="dx.exe"; on any non-Windows host the test is false, so under set -euo pipefail the &&-list returns 1, and as the function's final command that becomes its exit status — aborting the whole installer with zero output and nothing downloaded. Every 0.8.0 build 1 curl -fsSL https://dxcli.com/install.sh | bash on macOS/Linux was a silent no-op. It's now an if-statement, so the non-Windows path always returns 0.
  • install.sh local installs exit 0 again. Same set -e class, in the EXIT trap: cleanup()'s only line — [ -n "${CLEANUP_DIR:-}" ] && rm -rf … — returns 1 when nothing was downloaded, and a failing EXIT trap overrides the script's exit status. bash install.sh from a checkout or extracted tarball (README methods #2/#3) exited 1 right after printing "Done!", breaking anything that checks the exit code. curl | bash was unaffected (the download path sets CLEANUP_DIR). cleanup now ends with return 0.
  • dx snap / resume / fork / drop / log -r / ui now work outside a git repo. A session taken in a plain (non-git) folder is keyed by its absolute path — the same identity Claude Code uses for ~/.claude/projects/<mangled-path> — with empty commit/branch/remote in sessions.tsv. resume/drop scope to the folder by exact path (plus the existing JSONL-in-project-dir fallback); fork forks the session without cutting a branch and says so; log -r filters to the folder. Behavior inside a git repo is unchanged. enable / disable / init still require git — hooks are meaningless without it. Because these rows carry empty middle columns, every while read over sessions.tsv now goes through a \x1f reflow first — tab is IFS whitespace, so consecutive tabs would otherwise collapse and shift every later field (uuid into the agent column, etc). And dx snap refuses a folder path containing a literal tab or newline rather than write a corrupt TSV row.

0.8.0

Continuous sync.

Added

  • dx sync enable — start a background daemon that watches ~/.dx/data/ and ~/.claude/projects/ and pushes changes to the configured sync target. Defaults: 10s push debounce, 30s pull interval, overridable via --push-debounce N and --pull-interval N. Intervals are persisted to ~/.dx/run/sync.conf.
  • dx sync disable and dx sync status — stop and inspect the daemon. Status shows PID, target, last push/pull timestamps, configured intervals, and last error.
  • dx sync trigger — tell the running daemon to do an immediate push+pull, bypassing debounce and interval. Silent no-op if no daemon is running, so it's safe to wire into git hooks unconditionally.
  • Hook integration — dx enable now installs both a post-commit hook (existing behavior: auto-snap sessions on commit) and a pre-push hook. Both nudge the auto-sync daemon via dx sync trigger so your latest commit and its session ride along with the very next sync cycle instead of waiting for debounce. dx disable removes both hooks. Pre-existing hook content is preserved.
  • Aliases — dx watch / dx unwatch / dx watch-status and dx auto-sync on|off|status all dispatch to the same handlers.
  • Squash-push transport — the daemon force-pushes a single orphan commit per cycle so the sync repo never grows beyond one commit. Manual dx sync push is unchanged. Periodic git gc --prune=now every 50 pushes keeps the local clone small.
  • Cross-platform file watcher — inotifywait on Linux, fswatch on macOS, pure-bash polling fallback on Git-Bash / MSYS / Linux-without-inotify-tools / macOS-without-fswatch.
  • DX_WATCHER env var forces a specific watcher (inotify, fswatch, or poll) regardless of what's on PATH — useful for testing and for opting out of native watchers on a system where they misbehave.

Changed

  • Constants moved together near the top of the script; BLOBS_DIR is no longer defined mid-file.
  • New state directories ~/.dx/log/ and ~/.dx/run/ are created on demand and safe to delete at any time.

Fixed

  • dx resume / dx fork with an all-digit UUID prefix (e.g. dx resume 1111 for session 1111abcd-…) no longer misreads the argument as a list index and fails — index resolution falls back to prefix and name matching before giving up.
  • Cold-start dx resume <session> on a fresh device no longer exits silently when ~/.claude/projects doesn't exist yet — the session is restored from synced blobs as intended.

0.7.5

Quiet update nudge.

Added

  • Tip in install.sh. A single trailing line — Tip: run 'dx update' anytime to pull the latest release. — now follows the existing "Done!" message at the end of every install. Pure discovery, no state file, no network call.
  • Update availability nudge at the bottom of dx (no args) and dx version. Parses ${DX_VERSION:-X.Y.Z} out of https://dxcli.com/install.sh (the same trick dx update uses — no separate /latest endpoint, no Render rewrite-rule exception), at most once every 7 days. Caches the result in $DX_HOME/.latest-known plus a $DX_HOME/.last-update-check timestamp. When a newer version is published, prints one line to stderr:

    dx 0.7.6 is available — run 'dx update' to install

    Silent on every failure mode: no curl on PATH, network error, malformed response (a strict semver-shape [0-9]*.[0-9]*.[0-9]* check drops HTML / error pages / truncated downloads), current already ≥ latest (sort -V handles preview/dev installs correctly).

  • DX_NO_UPDATE_CHECK=1 env var disables the check entirely. DX_INSTALL_URL being set also skips it (preview installs shouldn't be told the production version is "available").

Why it's quiet by design

The git post-commit hook (dx hook-run), the snap/resume/fork/drop flows, and the table/sync commands (dx log, dx sync, dx ui) all skip the check by design — it only appears on the two attentive-but-non-task surfaces where a single trailing line is on-topic.

Build 2 — resume / fork / hop stop failing with Argument list too long

  • The interactive picker was handed the entire sessions JSON as a node command-line argument. Once you'd accumulated enough snaps, the blob exceeded the OS ARG_MAX limit and the picker launch died with node: Argument list too long (E2BIG) — dx resume (and the fork / hop aliases that share the picker) printed the error and cancelled before the picker could open. The sessions JSON is now written to a temp file and the path is passed instead, so the size of your session history no longer matters. picker.js accepts either an inline JSON array (backward compatible) or a path to a file holding it.

0.7.4

In-place update.

Added

  • dx update — re-runs the official installer in place so you can upgrade without remembering the curl one-liner. UX patterns synthesized from a survey of brew, gh, rustup, deno, bun, uv, mise, and atuin:
    • No-op short-circuit. If you're already on the latest published version, dx update exits with dx is already up to date (v0.7.4) — no wasted download. Reuses the version embedded in install.sh as the source of truth, so no separate /latest endpoint is needed and preview installers carry their own version naturally.
    • Version delta in the framing. A forward update prints Updating dx: v0.7.3 -> v0.7.4... so you see exactly what's about to change.
    • Pin / downgrade. dx update 0.7.2 (also dx update v0.7.2) installs that exact version. Useful for pre-1.0 rollback. The framing flips to Installing dx v0.7.2 (current: v0.7.4)... so the intent reads as install-this-specific-version, not "newer is better".
    • --force / -f bypasses the no-op short-circuit to repair a broken install.
    • Release-notes link. After a successful forward update against the production source, prints release notes: https://github.com/ghgugle/dx/releases/tag/v0.7.4 on its own line. Suppressed for pinned versions and preview installs.
    • Pairs naturally with dx _admin preview <N> from 0.7.3: try a PR preview build, then dx update back to the official release.
  • DX_INSTALL_URL env var overrides the installer source (default https://dxcli.com/install.sh). Useful for installs from a Render preview, and used internally by the test suite to mock the installer without hitting the network.

Build 2 — install.sh stops deleting the new dx.exe on Windows

  • install.sh's Windows cleanup was deleting the binary it had just written. On MSYS, when only dx.exe exists, rm -f dx auto-resolves to dx.exe — so the cleanup intended for the stale extensionless shim from pre-0.7.3 installs was actually shooting the freshly-installed binary in the foot. link_binary then crashed with cp: cannot stat $DX_HOME/dx.exe and set -e aborted, leaving which dx pointing at the old version. Both rm -f calls ($DX_HOME/dx and $BIN_DIR/dx) are now find -maxdepth 1 -type f -name "dx" -delete, which matches filenames literally and never auto-resolves. The first Windows install from 0.7.4 build 1 worked because a real stale dx still existed; every subsequent upgrade silently failed. Build 2 fixes it.

0.7.3

Install builds from deployment previews.

Added

  • dx _admin preview <N> (alias preview-<N>) — install the dx build from a Render PR preview deploy. Resolves https://dx-pr-<N>.onrender.com, then runs that preview's install.sh with DX_BASE_URL pointed back at it, so the install pulls exactly that PR's build with no dxcli.com round-trip and no Render dashboard config. --dry-run prints the resolved command without installing.
  • DX_BASE_URL in install.sh — overrides the tarball download base (default https://dxcli.com), so installs can target a preview or mirror.

Fixed

  • Windows install replaces dx.exe correctly. install.sh was copying $src/dx everywhere, but the Windows tarball ships dx.exe. MSYS quietly auto-resolved the source-side filename, so ~/.dx/dx.exe was getting written — but link_binary then propagated the stale extensionless ~/.dx/dx from prior installs onto ~/.local/bin/dx, and which dx returned the old version even after a "successful" upgrade. install.sh now picks the right binary name per platform (dx vs dx.exe) for every cp/chmod/exists-check, and deletes stale extensionless shims left behind by older installs.

Notes

  • _admin is a hidden developer/maintainer namespace, omitted from the main help output.
  • Provider URL resolution is factored out so non-Render preview hosts can be added later — see ROADMAP.md.

0.7.2

Resume finds sessions opened in a different repo.

Fixed

  • dx resume / dx fork / dx drop now match by JSONL existence when the row's stored repo doesn't match the cwd. If you open a session in repo B that was originally auto-snapped in repo A (Claude writes the JSONL into B's project dir on claude --resume), dx now surfaces that session as "current" in B. Previously the row's stored remote was checked against B's remote, didn't match, and dx reported "no sessions found for this repo" — while dx snap in the same folder happily said "already snapped". One symptom, two commands giving opposite answers; the JSONL existence check resolves it.

Unchanged

  • The existing match priority — git remote URL → cwd basename — is preserved. JSONL existence is a tertiary fallback, so on a clean install where every session is in its original repo, nothing about resume changes.

0.7.1

Resume sessions across repos.

Added

  • dx resume -a / --all — open the picker (or resolve a numeric index) across every saved session, not just sessions from the current repo. Use this to pick up a Claude conversation started in repo A while working inside repo B. The picker opens pre-toggled to all-projects mode; Ctrl+A still toggles it back.
  • dx fork -a / --all — same flag on dx fork for parity. Branch any saved session into a fresh one without first switching directories.

Changed

  • When the picker has no current-repo matches, the error message now suggests dx resume -a (or dx fork -a) instead of stopping cold.

0.7.0

Session forking.

Added

  • dx fork [id] — branch any saved session into a fresh one without touching the original. Wraps claude --resume <uuid> --fork-session, so the original UUID stays intact and Claude assigns a new ID to the fork. Reuses the same picker, numeric-index, and partial-UUID resolution as dx resume.
  • dx branch — alias for dx fork.

0.6.0

Internet-based session sync via private git repo.

Added

  • dx sync setup — one-step sync configuration. Auto-detects the gh CLI, creates or reuses a private <user>/dx-sessions GitHub repo, and writes the target to $DX_HOME/config. Falls back to a prompted git URL for users without gh.
  • Git-repo transport for dx sync push|pull — treats a private git repo as a free, store-and-forward relay across devices. Reuses the existing TSV union-merge and tombstone logic; git is only the transport. Supports HTTPS and SSH URLs on GitHub, GitLab, Bitbucket, Codeberg, and any self-hosted git.
  • Global sync config — sync=<url> in $DX_HOME/config, checked between DX_SYNC env var and per-repo .dx, so the relay is configured once per device.

Changed

  • dx sync push on a divergent target now fetches and re-integrates remote changes before pushing, retrying once on non-fast-forward rejection.
  • Legacy local-path and rsync targets continue to work unchanged.

0.5.7

Compiled binary distribution.

Changed

  • Binary build — dx ships as a compiled Go binary with XOR-encoded bash script, protecting source from casual inspection
  • Per-platform tarballs — dx-0.5.7-linux-amd64.tar.gz, darwin-arm64, darwin-amd64, windows-amd64
  • dx hook-run — post-commit hook logic merged into dx as a subcommand; dx-hook.sh is now a backward-compat shim
  • JS minification — picker.js and build-data.js minified with esbuild in release builds
  • Remote URL normalization — dx resume and dx log now match sessions across SSH and HTTPS clones of the same repo (e.g. [email protected]:user/repo and https://github.com/user/repo)
  • Resume picker order — newest sessions shown first in interactive picker

0.5.6

Session management.

Added

  • dx drop [uuid] — remove a snapped session from sessions.tsv and names.tsv without touching the original Claude session files. Supports interactive picker or direct UUID lookup.

0.5.5

Cross-device session sync, public website, curl-pipe installer.

Added

  • dx sync push|pull — sync sessions and JSONL blobs to a local path (USB, NAS, shared folder) or rsync remote. Renames and updates from the push side take precedence on merge.
  • Resume from synced blobs — dx resume checks ~/.dx/data/blobs/ for JSONL files pulled via sync when not found in Claude projects
  • curl-pipe install — curl -fsSL https://dxcli.com/install.sh | bash downloads the tarball and installs automatically
  • Public website at dxcli.com — docs page, download button, no private repo references
  • build-public.sh — assembles the public/ deploy folder with site files and release tarball

Changed

  • Install URLs updated from GitHub to dxcli.com
  • DX_SYNC env var or sync= in .dx config for default sync target
  • Copyright notice added to dx version output and site footer

0.5.0

Cross-folder and cross-OS session resume, redesigned installer.

Added

  • Cross-folder resume — dx resume now works when resuming a session from a different clone of the same repo. The session file is automatically copied into the current project's Claude directory.
  • Cross-OS resume (WSL ↔ Windows) — sessions created on Windows can be resumed from WSL and vice versa. Handles the path mismatch between C:\Users\... and /mnt/c/Users/... project directories.

Changed

  • Installer redesign — styled ==> output with platform detection, version display, and structured progress messages
  • Remote install — curl | bash now downloads tarball from GitHub releases with SHA-256 checksum verification (supports sha256sum, shasum, and openssl)
  • Windows binary linking — installer uses cp instead of ln -s on MINGW where symlinks are unreliable for Windows programs
  • Git-clone migration — installer auto-converts old git-clone installs to file-based installs

0.4.0

Resume picker UX improvements.

Changed

  • Session viewer redesigned from table to card layout with search and filter dropdown
  • Ctrl+C double-tap safety check in resume picker
  • Minimalistic search icon in resume picker
  • hop/zap aliases for dx resume, entity renamed to "snap" in picker
  • Responsive search, lighter focus styling, search footer in picker

0.3.0

Session viewer, snap/resume, chat history, and interactive picker.

Added

  • dx snap — save your current interactive Claude session (aliases: dx slap, dx yoink)
  • dx resume — resume a saved session from any checkout of the same repo
    • Interactive TUI picker with search, arrow-key navigation, and scroll
    • Ctrl+R to rename sessions with custom display names (persisted in ~/.dx/data/names.tsv)
    • Ctrl+A to toggle between current repo and all projects
    • Ctrl+B to toggle branch — shows current branch badge and folder paths per session
    • hop and zap aliases for dx resume
    • Entity renamed to "snap" in picker UI ("Resume Snap", "No matching snaps.")
    • Session detail line shows canonical repo name, commit, agent, type, and uuid
    • Lists sessions by number, matches repos by git remote URL
    • Warns if session's commit is ahead of current HEAD
  • dx ui — opens a local session viewer in your browser
    • Stats bar: total sessions, unique repos, snap count, agent breakdown
    • Filterable by repo, agent, and type (snap/auto)
    • Sortable columns with clickable headers
    • Click any session with a chat icon to view the full Claude conversation
    • Chat detail view with user/assistant messages, thinking blocks, tool calls
    • claude --resume <uuid> shown for easy copy
    • Responsive design matching the dx site style
  • app/template.html — self-contained HTML/CSS/JS template for the session viewer
  • app/build-data.js — Node script that reads sessions.tsv + Claude JSONL files, outputs JSON
  • Session type tracking: snap (manual) vs auto (post-commit hook)
  • Git remote URL stored in sessions.tsv for cross-folder repo matching
  • dx log (existing) — show recent agent sessions in terminal
  • Canonical repo identity — sessions from different folders of the same repo (same remote URL) now group under one name in UI and stats
  • Folder path shown as tooltip in UI table and as detail line in resume picker

Changed

  • sessions.tsv format extended with two new columns: type (snap/auto) and remote URL
  • install.sh copies app/ directory (template + build script) to ~/.dx/app/
  • install.sh creates ~/.dx/data/ directory on install
  • Post-commit hook now records session type as auto and includes git remote URL

How it works

  • dx snap finds the most recently modified Claude JSONL in ~/.claude/projects/<project>/ and records it
  • dx ui uses Node to parse sessions.tsv + Claude JSONL files, injects the data into an HTML template, and opens the result
  • dx resume matches sessions to the current repo by git remote URL (or basename fallback), then runs claude --resume <uuid>

0.2.0

Multi-agent support and Entire-style install.

Added

  • Multi-agent architecture: Claude Code, plus hook scripts for other coding tools
  • Modular agent extensions (agents/<name>.sh) with dx_agent_run contract
  • .dx config file for per-repo agent selection and args
  • DX_AGENTS and DX_<AGENT>_ARGS environment variable overrides
  • dx CLI with enable, disable, init subcommands
  • One-liner install: curl -fsSL .../install.sh | bash
  • Custom agent support — drop a .sh file in agents/

Changed

  • Renamed from claude-session-fork to dx
  • install.sh is now a global bootstrap (installs to ~/.dx, links CLI to PATH)
  • Per-repo setup via dx enable / dx disable instead of bash install.sh
  • Config parsing is generic — any <name>_args= maps to DX_<NAME>_ARGS

Removed

  • claude-session-fork.sh (backward compat handled by install migration)

Migration

  • Repos with the old # claude-session-fork hook guard are auto-migrated on dx enable

0.1.0

Initial release.

  • Git post-commit hook creating Claude Code sessions per commit
  • Deterministic UUID session IDs from commit hashes
  • Slug generation from commit messages
  • install.sh for per-repo hook installation