2 → 1 Dotfiles, Done with Cursor Cloud Agents
Note: I consider these devlogs my personal journal of what I’m learning, so I won’t be writing a full-fledged article here. Just learnings and thoughts concisely.
I had two GNU Stow trees. One public, one private. The split was supposed to keep secrets out of GitHub. In practice it meant two clones, two make installs, and a public history of nvim/tmux/kitty that did not need to be public.
I did not do the merge on the Mac. That machine still had ~/.dotfiles stowed. I used the pure-cloud path: a Cursor cloud agent with both repos checked out, steered over chat, on 18 Aug 2026.
The public GitHub repo is archived and private now. Everything lives in the private tree.
The split that existed
| Public | Private | |
|---|---|---|
| Repo | msharran/.dotfiles (master) |
msharran/.dotfiles-private (main) |
| Home path | ~/.dotfiles |
~/.dotfiles-private |
| What lived there | nvim, tmux, kitty, ghostty, k9s, sbin helpers, terminfo | fish, zed, jj, git-crypt secrets, agent tooling |
| Stow | GNU Stow into $HOME |
GNU Stow into $HOME |
Goal of the run: one private repo, a sunset of the public one, and a one-shot path for the machine that still has the public clone linked.
~/.dotfiles (stowed) ~/.dotfiles-private (stowed)
│ │
└──────── absorb once ───────┘
│
▼
~/.dotfiles-private only
~/.dotfiles parked under /tmp
Copy was cheap. Steering was the real part.
Public .config/ subtrees did not overlap the private ones, so the first pass was a straight copy: aerospace, bat, ghostty, k9s, kitty, nvim, zsh, sbin/, .tmux.conf, terminfo. Skipped the public GPL LICENSE (do not copyleft the private repo) and local junk like *.bak.
The public Makefile became a tombstone. make install / stow-link / dryrun / ls now print instructions and exit 1. make clean still exists, and only removes $HOME symlinks that still point at ~/.dotfiles, never at the private clone.
Design that survived contact with the machine
make install is cheap. Day-to-day refresh should not brew the world.
make bootstrap # dirs, brew, npm, terminfo — new Mac only
make install # git crypt unlock, stow, agent-guidance, SSH perms
A review pass also caught a fake brew formula (glowin vs glow), brew install a b c || true (one miss skipped the rest), and ~/projects (this machine keeps clones under ~/root/, not ~/projects).
Absorb is a skill you run once, not a hook on every rebase. The first version wired it into gsync (an agent skill: stash if needed, git pull --rebase, walk conflicts, ask before push), gpr (a fish abbreviation for git pull --rebase), fish startup, and git hooks. That would have rewritten gpr into a wrapper, for a one-time migration. Reverted to invoke-only: I type /absorb-public-dotfiles, and the skill calls the script.
/absorb-public-dotfiles
│
▼
~/.dotfiles-private/.claude/skills/absorb-public-dotfiles/absorb-public-dotfiles.sh
TL;DR. Park the old clone under /tmp instead of deleting it. Unused sbin helpers go to archive/ and are not stowed. Stow always targets $HOME. Absorb dry-runs into an empty home first, then unstows public links, then stows private.
I could not test macOS make install on this pod
The cloud instance is Ubuntu 24.04, nested KVM, no systemd, no Homebrew, no git-crypt, no Docker at boot. Egress unrestricted. That last point does not matter: this is not a Mac, and Apple will not hand it a recovery image.
I still tried. Docker was not installed; dockerd started by hand. overlay2 cannot mount on this overlay root; vfs is fine. A tiny public image runs. Then:
| Attempt | Result |
|---|---|
kvm-ok |
KVM acceleration can be used |
sickcodes/docker-osx:latest (Catalina, VNC, 6G RAM) |
Apple osrecovery.apple.com HTTP 403; no BaseSystem.img; QEMU exits |
sickcodes/docker-osx:auto / :big-sur |
Docker Hub 404 (tags gone) |
dockurr/macos Ventura, web UI :8006 |
same Apple 403, container exit 60 |
dockur’s README is also clear: Apple’s EULA does not permit installing macOS on non-Apple machines. Even if the 403 lifted, it would not have been a license-compliant guest. Docker-OSX and dockur/macos were the wrong test for this pod.
Isolated Linux $HOME instead
HOME=/tmp/dotfiles-test-home
clone=$HOME/.dotfiles-private
make dryrun && make install
git crypt unlock || true no-ops as designed. make bootstrap (brew) was not run.
That is how the Stow parent-directory bug showed up. stow -v . from an off-home checkout at /tmp/dotfiles-private-test — not $HOME/.dotfiles-private — linked into /tmp, not $HOME. Those stray links were unstowed immediately, then -t "$(HOME)" landed.
After that, install passed: .tmux.conf, .config (nvim/kitty/fish/…), sbin → private, LaunchAgents present, archive/ not stowed. The live repo clone stayed clean because the test used a copy.
Then I ran absorb on the Mac
After the cloud PRs, I invoked /absorb-public-dotfiles once on the machine that still had ~/.dotfiles stowed. The skill called the script against live $HOME. Exit 0. Public links unstowed, private restowed, clone parked at /tmp/dotfiles-public-$USER-20260818184055.
Redacted chat: Absorb transcript.
make bootstrap only if this is a new Mac.
The public GitHub repo is archived and private. Git history from the old public clone still exists there; the live tree is private only.