feat(sync): cross-device GitHub sync — v3 lean rebuild#3
Conversation
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Supersedes v1/v2: cuts the custom GitHub App and the 3-platform heartbeat. Token-paste auth, rebase-model tick, agent-agnostic sync trigger via sync-hook + an AGENTS.md rule. Finishes the phone-write inbox path v2 left unbuilt. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces ffPull (which treated any divergence as an emergency and hard-reset local work to origin) with a rebase model. Tick now commits local changes first, then git pull --rebase: with the inbox pattern, disjoint-file commits from two devices rebase cleanly with no orphan branch. The branch-and-reset escape hatch is kept ONLY for a genuine same-file rebase conflict, logged as sync-conflict. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Drops the custom GitHub App entirely — no App registration, no verification, no privacy-policy obligation, no single point of failure, no unhandled 8-hour token expiry. Setup now opens a pre-filled github.com token link; the user pastes a classic repo-scope PAT, which is validated via GET /user. Also removes the placeholder-client-id hard-fail in runSetup — the cause of the bug where 'dotaios setup' exited 1 on success. The setup wizard now closes its own readline before the paste flow opens one, avoiding two readline interfaces on one stdin. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Deletes heartbeat.mjs (launchd / systemd / schtasks installers) and its tests, plus the heartbeatPlistPath / heartbeatUnitDir path helpers. The OS-level installers were the most fragile part of the feature, and the 5-minute timer's only unique job — pulling while the machine is idle — has no user value. Sync now fires from two agent-agnostic mechanisms instead: the per-command hook (sync-hook.mjs) and an AGENTS.md rule (added in a following commit). Updates help text and stale comments accordingly. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…rules Completes the two-way design v2 left unbuilt. Adds the process-inbox skill: a local agent reads notes that arrived from another device in memory/inbox/, files each into the right vault/context location, then removes the inbox file. Two rules added to the AGENTS.md template — one to run process-inbox when memory/inbox/ has files, one to run 'dotaios sync tick' at session start/end. AGENTS.md is read by every agent, so the sync trigger is vendor-agnostic with no hook code. inbox.mjs (a JS helper listed in the v2 file map) is intentionally omitted: the skill is markdown an agent runs with its own file tools, so a helper module with no caller would be dead code. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Documents dotaios sync: the one-time PAT setup, reading the repo from a phone, the memory/inbox phone-write path, and the broad-scope classic-token tradeoff with the fine-grained alternative. Adds the sync command to the commands table. No daemon, no GitHub App. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two bugs found by inspection before the live smoke test: 1. Exit-code leak (the bug class behind Task 16, only partly fixed). Removing the placeholder block killed one trigger, not the leak: a genuine sync failure inside the 'dotaios setup' wizard still set process.exitCode=1, so the whole wizard exited 1 even though setup otherwise succeeded. runSetup now THROWS on failure and never touches process.exitCode. 'dotaios sync setup' (the dispatcher) catches and exits non-zero — correct for a standalone command. The wizard catches, logs, and lets setup finish 0 — correct for an optional step. 2. 'dotaios sync setup' ignored --path. runSetup now parses --path like 'sync tick' does, so a non-default AIOS folder can be synced (and smoke-tested) without touching ~/aios. runSetup gains an injectable orchestrate override for testing. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
dotaios init copies the templates/ tree into ~/aios/, which dragged templates/sync-gitignore.template in as a stray literal file. That template is a build-time resource read by 'dotaios sync setup' to write the synced repo's .gitignore — it has no place in the user's folder. Excluded it from the init copy, alongside aios.json. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Step 1 prints the full token URL but step 2 only said 'github.com/new (pre-filled)'. If the browser-open lands on the wrong window the user had no URL to copy. Now both steps print their full URL. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two cosmetic inaccuracies in the last_push_sha status field, both found during the live smoke test: - After 'sync setup', the field stayed null: the initial mirror push runs via initialMirrorPush, which bypasses tick, so tick's first run had nothing to push. initialMirrorPush now returns the commit sha and setup records it. - After a tick whose rebase replayed commits, the field held commitAll's PRE-rebase sha, which no longer exists in history once the rebase rewrites it. Tick now reads HEAD after the push. Both verified live: last_push_sha matches the GitHub HEAD sha. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ser config A non-technical user's machine often has no global git user.name / user.email. Without them 'git commit' fails 'Author identity unknown' and 'dotaios sync setup' dies at the initial-push step. The stubbed tests and the smoke test (on an already-configured machine) both missed this. createGit now sets GIT_AUTHOR_* / GIT_COMMITTER_* in the spawn env on every git call, so sync is self-sufficient. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The token is embedded in the origin remote URL, so it also lives in <aios>/.git/config at mode 0644 — but 'dotaios sync logout' only removed ~/.dotaios/sync.json and printed 'Signed out', leaving a valid token on disk. logout now also removes the origin remote and tells the user to revoke the token on GitHub (the only way to kill it server-side). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…error GitHub's create-repo page has 'Initialize this repository' options (README / .gitignore / license). If the user ticks one, the repo has a commit and the initial mirror's 'git push' is rejected non-fast-forward — setup died with an unreadable git error at step 3. setup step 2 now explicitly says to leave those options off, and pollForRepoExists checks the repo is empty (GitHub returns 409 for an empty repo's commits) — a non-empty repo now fails with a plain 'delete it and retry' message before any push is attempted. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
… line - dotaios setup --path X now passes X to the sync sub-step (it was hardcoded to ~/aios). - Remove inboxDir() from paths.mjs — orphaned when inbox.mjs was cut, referenced only by its own test. - Drop the stale memory/.daemon.* line from sync-gitignore.template; the daemon was removed in v3. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Audit finding 4: a same-file two-device conflict parks the local edit on a local-<ts> branch that sync status does not surface. Deliberately not built for v3 — the conflict path is near-unreachable for the one-desktop-plus-phone ICP, and the event is already logged to events.jsonl. Documented as a known limitation with the trigger for revisiting it (multi-desktop users). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Pre-merge review pass — 3 bugs fixed, branch merge-readyClosing out the feature. Three bugs from pre-merge review, verified real against current code (systematic-debugging), each fixed test-first: Fix 1 — sync had no git identity ( Fix 2 — logout left the live PAT on disk ( Fix 3 — initial push failed on a non-empty repo ( Also evaluated
Verification Merge decision left to @filocosta46 — not merging from automation. |
Summary
Adds
dotaios sync— mirrors the local~/aios/folder to a private GitHub repo so a phone-side AI can read the same memory. Two-way: phone writes land inmemory/inbox/, and theprocess-inboxskill files them on the next desktop session.This is v3 of the feature. A v2 build (~16 tasks) was reviewed and found overbuilt; v3 cuts the overbuild:
repo-scope PAT. No infrastructure DotAIOS owns, no app verification, no single point of failure, no unhandled token expiry.AGENTS.mdrule — agent-agnostic, zero per-vendor code.commit → pull --rebase → push. Branch-and-reset is a fallback for a genuine same-file conflict only, not the default for every divergence (v2 silently hard-reset local work).Zero new dependencies;
packages/corestays zero-dep. Net result: the feature is smaller than v2.Design docs
docs/superpowers/specs/2026-05-21-github-sync-v3.mddocs/superpowers/plans/2026-05-21-github-sync-v3.mdVerification
process-inboxfiles it → inbox cleared; two-device divergence → clean rebase, no orphan branch, no conflict markers.fix(sync): ...commits).Known limitation
A same-file two-device conflict parks the local edit on a
local-<ts>branch thatsync statusdoes not surface. Near-unreachable for the one-desktop-plus-phone ICP; documented in the v3 spec.Test plan
node --test tests/**/*.test.mjsgreendotaios sync setupend-to-end on a fresh machine with no global git identity🤖 Generated with Claude Code