sc-sync
Skill13 starsChanged 2 months agoMITView on GitHub
rsync gitignored files between a VPS and a local machine over Tailscale, for a repo checked out (mirrored path) on both. Two directions: vps-local and local-vps, selected by SYNC_ROLE crossed with the requested direction (never by sniffing hostname). Always dry-run first, --apply to actually copy. Trigger on /sc-sync, 'sync vps to local', 'sync local to vps', 'tailscale sync', 'gitignored files between machines'.
- Commits and pushes
In this repository28 other files it gives its agents.
- AGENTS.mdAGENTS.md
- si-coderSkill
- sc-allSkill
- sc-axSkill
- sc-buildSkill
- sc-cfSkill
- sc-clerkSkill
- sc-convex-cloudSkill
- sc-convexSkill
- sc-dokploySkill
- sc-dokuSkill
- sc-dxSkill
- sc-feSkill
- sc-gitSkill
- sc-helpSkill
- sc-installSkill
- sc-n8nSkill
- sc-onboardingSkill
- sc-providerSkill
- sc-resendSkill
- scSkill
- sc-skillSkill
- sc-stripeSkill
- sc-supabaseSkill
- sc-uiSkill
- sc-uxSkill
- sc-vercelSkill
- use-si-coderSkill
The file
We split this skill into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 121
Never skip straight to
--apply: Run without it, show/summarize the diff, and only re-run with --apply once the diff has actually been …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
/sc-sync — Gitignored-file sync over Tailscale
Why this exists
A repo is checked out on two machines (say, a VPS and a laptop). .gitignore
deliberately keeps some files out of git — local notes, generated docs,
scratch config — so they never travel with git push / git pull. Plain SSH
scp/rsync between the two kept failing (NAT, dynamic IP, firewall...);
Tailscale is confirmed working on both ends. This skill wraps rsync over the
Tailscale interface so those gitignored files can move in either direction
without inventing a new transport.
It deliberately does not try to guess which machine it's running on via
hostname — that leaks a real, personally-identifying hostname into logs and
breaks the moment either machine is renamed or reimaged. Instead the
invoking side declares itself explicitly via SYNC_ROLE.
Requirements
- Both machines joined to the same tailnet (
tailscale statusshows both). - The repo is checked out at the same absolute path on both machines (the
default
SYNC_REMOTE_PATHassumes this — override it if the layout differs). rsyncinstalled on both machines.- The non-invoking side needs inbound SSH reachable over Tailscale — rsync
tunnels through SSH by default. That means:
- Linux/macOS:
sshdrunning, reachable from the tailnet. - Windows: OpenSSH Server is NOT on by default. You must explicitly
enable "OpenSSH Server" (Settings → Optional Features, or
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0in an elevated PowerShell) and make sure thesshdservice is running before alocal-vps/vps-localsync can reach a Windows box. This is a real per-machine caveat, not a generic assumption — check it on whichever side is the pull/push target if the connection times out.
- Linux/macOS:
Env vars
| Variable | Required | Default | Meaning |
SYNC_ROLE | yes | — | Which machine this process is running on: exactly vps or local. Never inferred from hostname. |
SYNC_VPS_TS_ADDR | yes | — | Tailscale MagicDNS alias or 100.x IP of the VPS, e.g. your-vps or 100.x.x.x. |
SYNC_LOCAL_TS_ADDR | yes | — | Tailscale MagicDNS alias or 100.x IP of the local machine, e.g. your-laptop. |
SYNC_REMOTE_USER | no | os.userInfo().username | SSH user on the far side. |
SYNC_REMOTE_PATH | no | this repo's own git rev-parse --show-toplevel | Absolute repo path on the far side. Only override if the two checkouts don't live at the same path. |
Direction routing
Direction (vps-local or local-vps) says which way data should flow.
SYNC_ROLE says which machine is running the command right now. Crossing the
two tells the script whether this machine is pushing (source) or pulling
(destination) — the routing table is a small pure function
(route() in scripts/_shared.js), unit tested for all four cells:
| direction | SYNC_ROLE | this machine is | other machine is |
vps-local | vps | SRC — pushes to local | DST |
vps-local | local | DST — pulls from vps | SRC |
local-vps | local | SRC — pushes to vps | DST |
local-vps | vps | DST — pulls from local | SRC |
Connection-safe configuration
SYNC_* values may be stored in the user's sync named connection. In that case run the helper through sc run -- ... rather than exporting the values globally. The export examples below are legacy/manual configuration examples only.
Usage
# On the VPS, set once (e.g. ~/.bashrc):
export SYNC_ROLE=vps
export SYNC_VPS_TS_ADDR=your-vps
export SYNC_LOCAL_TS_ADDR=your-laptop
# On the local machine, set once:
export SYNC_ROLE=local
export SYNC_VPS_TS_ADDR=your-vps
export SYNC_LOCAL_TS_ADDR=your-laptop
cd ~/projects/myrepo
# Preview only (default — no files are copied):
node skills/sc-sync/scripts/sync.js vps-local
node skills/sc-sync/scripts/sync.js local-vps
# Actually copy, after reviewing the dry-run diff above:
node skills/sc-sync/scripts/sync.js vps-local --apply
node skills/sc-sync/scripts/sync.js local-vps --apply
# Sync only specific paths instead of the full default set:
node skills/sc-sync/scripts/sync.js local-vps --apply docs/private-notes.md scratch/The same command works from either machine for a given direction — it's the
env's SYNC_ROLE (not an argv flag) that tells the script whether it's
pushing or pulling this time.
What gets synced by default
Everything git ls-files --others --ignored --exclude-standard reports for
the repo (i.e. every gitignored-but-present file), excluding anything
under: node_modules, .git, dist, build, .next, .turbo, .venv,
venv, __pycache__, target, vendor, coverage, .cache — those are
build output / VCS internals / language caches, never something you want
hand-carried between machines. Pass explicit paths on the CLI to sync a
specific subset instead of the full default list.
Agent responsibility
- Dry-run first, always. The default (no
--apply) runsrsync --dry-runso the agent (and the human) can read exactly what would change before anything is copied. - Never skip straight to
--apply. Run without it, show/summarize the diff, and only re-run with--applyonce the diff has actually been reviewed — don't add--applyon the first invocation just because the user asked to "sync". --applyusesrsync -avzu(-u= skip files newer on the destination), so a newer file on the far side won't be clobbered by an older local copy — but that's a safety net, not a substitute for reading the dry-run diff.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

