quickstart

wisp ·

Named slots of your uncommitted work, mirrored live to Dropbox, switchable like branches. Iterate without committing or pushing, while everything stays backed up.

Think of wisp as git branches for work you haven't committed. You keep several separate piles of changes per repo, each one auto-backed-up to Dropbox, and you switch between them. Your changes never become commits and never touch git history.

The mental model

Install

# from the repo where wisp lives
cd ~/Documents/GitHub/wisp
./install.sh        # links `wisp` onto your PATH + loads the background daemon
wisp doctor          # sanity-check Dropbox, the daemon, and your setup

Requires macOS, git, and Node 18+. The daemon survives logout and reboot. ./uninstall.sh removes it (your slots are kept).

Everyday use

CommandWhat it does
wispSame as wisp default — activates (or creates) the slot named default. Your quick "just start tracking this."
wisp <slot>Switch: save the active slot, then make the tree equal <slot>. Creates it from your current work if new.
wisp switchToggle back to the slot you were on before this one (a cd - for slots), on the current branch.
wisp statusCurrent branch, active slot, mirror path, dirty-file count, tracking state.
wisp list (ls)Every branch's slots, this branch first (active one marked). In a terminal, arrow keys + enter restore a slot, switching branch first if it's on another one.
wisp syncForce an immediate mirror of the active slot right now.
wisp doctorDiagnose Dropbox reachability, the daemon, state, and mirror mapping.

A normal day:

cd ~/code/api
wisp redesign       # name your current half-finished work "redesign"; now auto-tracked
# ...edit, edit — every change mirrors to Dropbox in the background...
wisp spike          # saves "redesign", starts a fresh slot from your current work
wisp redesign       # switch back; your tree becomes exactly what "redesign" held
wisp                # with no slot in mind, just track under "default"

The three "stop" verbs

Easy to confuse, so they're deliberately distinct:

CommandWorking treeSlotsTracking
wisp --clearreverted to clean HEADall keptstopped
wisp detachleft exactly as-is (still dirty)all keptstopped
wisp drop <slot>untouchedthat one slot deletedunchanged

See exactly what will happen first

Every destructive command takes --dry-run. It prints the precise plan and writes nothing at all — not even the safety save. Use it freely before any switch or clear.

wisp redesign --dry-run   # shows what it would restore/remove, changes nothing

Safety model — how it protects your work

Data loss is treated as the worst possible outcome. The guarantees:

Built-in safeguards

Using wisp alongside git branches & stash

wisp never overwrites your working-tree files on its own. The background daemon is strictly one-way: it copies your tree into Dropbox and never the other way around. The only commands that rewrite your working tree are wisp <slot> and wisp --clear, and both save your current work into a slot first. So nothing in wisp can clobber uncommitted files behind your back — if you leave changes in the tree and do other things, wisp just keeps backing them up.

Slots are scoped to each branch

Each branch gets its own set of slots, so the same name on two branches holds two different piles and one branch's work never leaks into another's. When you change branches (e.g. in GitHub Desktop), wisp follows automatically: it notices HEAD moved, re-points to the new branch, and keeps backing up your work there — into that branch's own slot (a fresh branch starts a default slot). Nothing leaks across branches, because every branch's slots live in their own folder; the branch you left stays frozen exactly as it was. (While HEAD is detached there's no branch to track, so wisp refuses until you check one out.)

The one interaction to know: git stash empties the active slot

git stash moves your changes out of the working tree, so the active slot — which mirrors the current uncommitted set — syncs to "nothing uncommitted" and prunes those files from the slot. Your bytes are not destroyed: git's stash holds them, and Dropbox keeps the slot's version history, so git stash pop brings them back and wisp re-mirrors them. But to deliberately set work aside, prefer wisp <other-slot> — a safe save-and-restore that keeps everything visible in wisp — over git stash. Rule of thumb: drive a given pile of changes with either wisp slots or git stash, not both at once.

Things to be wary of

None of these will silently eat your work, but know them:

"Backed up" means on local disk, not confirmed uploaded

wisp writes your slot into the local Dropbox folder and flushes it to disk; Dropbox then uploads on its schedule. The daemon mirrors a change within a couple of seconds, and a safety sweep backstops that at 45 seconds at most. So the at-risk window is "edits in the last ~45s" plus "whatever Dropbox hasn't uploaded yet." For anything you truly cannot lose, a real git commit somewhere is still the strongest backup.

One slot, one machine at a time

The active-slot pointer is local to each machine, so machines don't fight over it, and slot contents sync through Dropbox. But actively editing the same slot on two machines at once isn't supported — Dropbox may make conflicted copies. wisp warns you when you take over a slot another machine last wrote. Let Dropbox settle between machines.

gitignored files are not in slots

By design (matching how your old backup script worked), wisp only tracks the uncommitted set git can see: modified, new (non-ignored), staged, and deleted files. Gitignored files — build output, .env, node_modules — are never mirrored. If a secret lives only in a gitignored file, wisp is not backing it up.

Staging is preserved best-effort

If you stage partial changes (git add -p), wisp restores that staged state on a switch. The one exception: if HEAD moved (you committed/pulled/rebased) between saving a slot and switching back to it, the staging may not replay — your file contents always come back correctly, but as unstaged changes, with a warning. Re-staging is quick.

It's brand-new software

wisp is v0.1. The known data-loss edge cases have been fixed and it has a full test suite, but treat it as new: keep genuinely irreplaceable work committed somewhere too, and lean on --dry-run when in doubt. If a switch ever refuses, that's the safety net working — read the message; it tells you the safe way forward.

Where things live

If something looks wrong

wisp doctor          # checks Dropbox, daemon, state, mirror mapping
wisp status          # what's active, dirty count, tracking on/off
wisp list            # every slot for this repo
# daemon logs:  ~/.wisp/daemon.log

A refused command is wisp protecting you — the message always names the safe next step (usually: name your work with wisp <name>, or wisp --clear).