Skip to content

feat(scripts): auto-sync — real-time bidirectional git sync per repo#41

Open
abdout wants to merge 3 commits into
mainfrom
feat/auto-sync-service
Open

feat(scripts): auto-sync — real-time bidirectional git sync per repo#41
abdout wants to merge 3 commits into
mainfrom
feat/auto-sync-service

Conversation

@abdout
Copy link
Copy Markdown
Contributor

@abdout abdout commented May 18, 2026

Summary

A background service that keeps every databayt repo at ~/<name> continuously synced with GitHub. Local commits push within ~2s; remote commits pull within ~60s. No manual git push/git pull for the common case.

Implements the "Real-time (file watcher)" sync model — the option you picked in the design question.

How it works

                    ┌─────────────────────────────┐
                    │     FileSystemWatcher /     │      Local commit
   .git/refs/heads ─┤        fswatch /            ├─►   debounce 2s
                    │       inotifywait           │      git push
                    └─────────────────────────────┘

                    ┌─────────────────────────────┐
                    │      Polling loop           │      Every 60s:
                    │      every PollInterval     ├─►   git fetch
                    │                             │      git pull --rebase
                    └─────────────────────────────┘      (skipped if dirty)

Per-repo isolation — one repo failing (no upstream, auth broken, force-push) never stops the others. Each iteration catches its own exceptions.

Working-tree-sacred — if git status --porcelain shows changes, the pull is skipped. Your in-flight work is never touched.

Conflict-safe pullgit pull --rebase fails → git rebase --abort → log error → next cycle tries again.

Files added

File Purpose LOC
.claude/scripts/auto-sync.ps1 Windows runtime — FileSystemWatcher + polling loop 193
.claude/scripts/auto-sync.sh macOS/Linux runtime — fswatch / inotifywait + polling 171
.claude/scripts/auto-sync-install.ps1 Register as logon-triggered Task Scheduler task 110
.claude/scripts/auto-sync-install.sh Register as launchd LaunchAgent (mac) / systemd user unit (linux) 143
content/docs/auto-sync.mdx User-facing doc 140

Total: 757 lines added across 5 new files, zero existing files modified.

Install (user-scoped, no admin/sudo)

# Windows — Task Scheduler "At logon", restarts on failure 3×
& "$env:USERPROFILE\.claude\scripts\auto-sync-install.ps1" -Install
# macOS — launchd LaunchAgent with KeepAlive=true
~/.claude/scripts/auto-sync-install.sh --install

# Linux — systemd user service with Restart=always
~/.claude/scripts/auto-sync-install.sh --install

Configuration (flags)

Flag Default What it does
-PollInterval / --poll-interval 60s How often to fetch+pull each repo
-Debounce / --debounce 2s How long after a local commit before pushing
-DryRun / --dry-run off Log only, no actual git operations
-Once / --once off Exit after one poll cycle (for testing)
-Verbose / --verbose off Echo every log line to console

Logs

Format: [HH:MM:SS] [LEVEL] [repo-name ] message

Stored at ~/.claude/logs/auto-sync-<date>.log. Levels: INFO, PUSH, PULL, WARN, ERROR.

What it does NOT do

  • Doesn't commit for you — only pushes existing commits
  • Doesn't resolve conflicts — aborts the rebase and logs
  • Doesn't force-push (history rewrites need manual handling)
  • Doesn't touch branches other than the currently-checked-out one

The doc page has a "what auto-sync does NOT do" section that's explicit about this — auto-sync is a co-pilot for the linear-commit common case, not a magic merger.

Relationship to maintain

maintain runs once daily at 09:00 (full doctor + sync-repos burst). auto-sync runs continuously between maintain runs. Run both — they complement each other.

Test plan

  • PowerShell parse-check: [PSParser]::Tokenize clean on both scripts
  • Bash syntax check: bash -n clean on both scripts
  • Live: install on Windows, make a commit in ~/kun, see PUSH log within ~5s
  • Live: push a commit to a repo from another machine, see PULL log within ~70s on this machine
  • Live: install on macOS via launchd, verify launchctl list shows the agent
  • Live: install on Linux via systemd user, verify systemctl --user status kun-auto-sync shows active
  • Dirty-tree case: make uncommitted changes, push from elsewhere, verify WARN + skip
  • Conflict case: induce a rebase conflict, verify abort + log + retry behavior
  • Auth-failure case: revoke gh auth, verify WARN per push attempt, no crash

Dependencies

Targets main directly. No dependency on the bootstrap chain (PRs 29-36, 37, 38). Works with both ~/oss/<name> (current main) and ~/<name> (PR #38) layouts — reads ~/.claude/memory/repositories.json first, falls back to canonical list otherwise.

Why this completes the "auto-synced" requirement

The user requirement was: "I expect all databayt org repos to be in the root and synced with github with auto synced." PR #38 puts them at root. This PR makes them auto-synced. Together they fulfill the requirement for every teammate.

Refs #28

🤖 Generated with Claude Code

Adds a background service that watches every databayt repo at ~/<name>
and keeps it in sync with origin without manual intervention. Local
commits push within ~2s; remote commits pull within ~60s.

The design implements the user-chosen "real-time + polling" model:

  Push side (real-time):
    FileSystemWatcher (Windows) / fswatch (macOS) / inotifywait (Linux)
    on .git/refs/heads. On change → 2s debounce → git push.

  Pull side (polling):
    Every 60s per repo: git fetch + git pull --rebase. Skipped when
    the working tree is dirty (in-flight work is sacred). On conflict,
    git rebase --abort is called and the failure logged.

  Per-repo isolation:
    One repo failing (auth, no upstream, force-push) doesn't stop the
    others — each iteration of the loop catches its own exceptions.

Files:

  .claude/scripts/auto-sync.ps1            193 LOC — Windows runtime
  .claude/scripts/auto-sync.sh             171 LOC — macOS/Linux runtime
  .claude/scripts/auto-sync-install.ps1    110 LOC — Task Scheduler register
  .claude/scripts/auto-sync-install.sh     143 LOC — launchd / systemd-user
  content/docs/auto-sync.mdx               full user-facing doc page

Install (user-scoped, no admin/sudo):

  # Windows
  & "$env:USERPROFILE\.claude\scripts\auto-sync-install.ps1" -Install

  # macOS / Linux
  ~/.claude/scripts/auto-sync-install.sh --install

Service registration:

  Windows: Task Scheduler 'At logon' trigger, restart on failure 3×,
           allowed on battery, no execution time limit, runs hidden.
  macOS:   launchd LaunchAgent with KeepAlive=true + RunAtLoad=true.
  Linux:   systemd --user service with Restart=always + RestartSec=10,
           WantedBy=default.target.

Logs at ~/.claude/logs/auto-sync-<date>.log with structured levels
(INFO / PUSH / PULL / WARN / ERROR) per repo per event.

Reads ~/.claude/memory/repositories.json for the repo list; falls
back to the canonical 11-repo set if absent. Works with both the
old ~/oss/<name> layout and the new ~/<name> layout (PR #38).

Both scripts validated: `bash -n` clean, PowerShell parse clean.

Refs #28

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@vercel
Copy link
Copy Markdown

vercel Bot commented May 18, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
kun Ready Ready Preview, Comment May 18, 2026 5:46am

PowerShell's [CmdletBinding()] auto-provides -Verbose as a common
parameter, so declaring [switch]$Verbose explicitly raises a runtime
'parameter defined multiple times' error on invocation. Drop the
explicit declaration and detect verbose mode via $VerbosePreference —
the user-facing flag name (-Verbose) is unchanged.

Verified on a live machine: -DryRun -Once -Verbose -PollInterval 5
now runs cleanly, discovers all 11 repos at ~/<name>, completes one
poll cycle, and exits without warnings.

Refs #28

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace em-dash/middle-dot/box-drawing/emoji chars with ASCII equivalents.
PS 5 (powershell.exe used by Task Scheduler) reads .ps1 as Windows-1252
without a BOM, so UTF-8 multibyte chars caused 'string is missing
terminator' parse errors and the task exited 1 right after launch.

Verified end-to-end on Windows: powershell.exe runs clean, scheduled
task State=Running, 11 repos tracked.

Refs #28

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant