agentleFS
rahmanef63rahmanef63/si-coder-agent/skills/sc-sync/SKILL.md

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

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
  1. Very specific:

    Never skip straight to --apply: Run without it, show/summarize the diff, and only re-run with --apply once the diff has actually been …

    line 121

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

/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 status shows both).
  • The repo is checked out at the same absolute path on both machines (the default SYNC_REMOTE_PATH assumes this — override it if the layout differs).
  • rsync installed on both machines.
  • The non-invoking side needs inbound SSH reachable over Tailscale — rsync tunnels through SSH by default. That means:
    • Linux/macOS: sshd running, 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.0 in an elevated PowerShell) and make sure the sshd service is running before a local-vps/vps-local sync 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.

Env vars

VariableRequiredDefaultMeaning
SYNC_ROLEyes—Which machine this process is running on: exactly vps or local. Never inferred from hostname.
SYNC_VPS_TS_ADDRyes—Tailscale MagicDNS alias or 100.x IP of the VPS, e.g. your-vps or 100.x.x.x.
SYNC_LOCAL_TS_ADDRyes—Tailscale MagicDNS alias or 100.x IP of the local machine, e.g. your-laptop.
SYNC_REMOTE_USERnoos.userInfo().usernameSSH user on the far side.
SYNC_REMOTE_PATHnothis repo's own git rev-parse --show-toplevelAbsolute 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:

directionSYNC_ROLEthis machine isother machine is
vps-localvpsSRC — pushes to localDST
vps-locallocalDST — pulls from vpsSRC
local-vpslocalSRC — pushes to vpsDST
local-vpsvpsDST — pulls from localSRC

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) runs rsync --dry-run so 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 --apply once the diff has actually been reviewed — don't add --apply on the first invocation just because the user asked to "sync".
  • --apply uses rsync -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.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.