Step 1 of 5
Prepare Git authentication
Use GitHub CLI over HTTPS so the LaunchAgent can authenticate through the macOS Keychain without depending on an interactive SSH agent.
gh auth status || gh auth login
gh config set git_protocol https
gh auth setup-git
git config --global user.name "YOUR_NAME"
git config --global user.email "YOUR_EMAIL"Step 2 of 5
Preserve the local library
Quit Codex, Cursor, and any agent currently reading skills. Move the existing local directory aside, then clone the VPS-backed repository into the canonical ~/.agents path.
backup_dir="$HOME/.agents.backup.$(date +%Y%m%d-%H%M%S)"
if [ -d ~/.agents ]; then mv ~/.agents "$backup_dir"; fi
git clone https://github.com/YOUR_ACCOUNT/stackenv-skills.git ~/.agents
echo "Previous local skills: $backup_dir"Step 3 of 5
Install the same worker
Both machines use the identical audited script and private Git branch. Run it once interactively before scheduling it.
mkdir -p ~/.local/bin
curl -fsSL https://stackend.codelynx.dev/skills-sync.sh -o ~/.local/bin/stackenv-skills-sync
chmod 700 ~/.local/bin/stackenv-skills-sync
~/.local/bin/stackenv-skills-syncStep 4 of 5
Schedule it with launchd
The LaunchAgent starts after login and runs every two minutes. Logs stay in ~/Library/Logs/StackEnv.
mkdir -p ~/Library/LaunchAgents ~/Library/Logs/StackEnv
cat > ~/Library/LaunchAgents/dev.stackenv.skills-sync.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><dict>
<key>Label</key><string>dev.stackenv.skills-sync</string>
<key>ProgramArguments</key><array>
<string>/bin/zsh</string><string>-lc</string>
<string>$HOME/.local/bin/stackenv-skills-sync</string>
</array>
<key>RunAtLoad</key><true/>
<key>StartInterval</key><integer>120</integer>
<key>StandardOutPath</key><string>LOCAL_LOG_PATH/stackenv-skills-sync.log</string>
<key>StandardErrorPath</key><string>LOCAL_LOG_PATH/stackenv-skills-sync.error.log</string>
</dict></plist>
EOF
sed -i '' "s|LOCAL_LOG_PATH|$HOME/Library/Logs/StackEnv|g" ~/Library/LaunchAgents/dev.stackenv.skills-sync.plist
launchctl bootout gui/$(id -u) ~/Library/LaunchAgents/dev.stackenv.skills-sync.plist 2>/dev/null || true
launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/dev.stackenv.skills-sync.plistStep 5 of 5
Verify both directions
Create a harmless test file on the computer, run a sync, then confirm it arrives on the VPS after the next timer execution.
touch ~/.agents/.sync-check
~/.local/bin/stackenv-skills-sync
launchctl kickstart -k gui/$(id -u)/dev.stackenv.skills-sync
tail -n 30 ~/Library/Logs/StackEnv/stackenv-skills-sync.log
git -C ~/.agents status --short --branchOn the VPS, run systemctl --user start stackenv-skills-sync.service, check that .sync-check exists, then remove it and sync once more.
~/.agents is synchronized. Project repositories, StackEnv configuration, tokens, and screenshot bypass keys stay machine-local.If Git reports a conflict
The worker stops and will not stage anything else while a rebase, merge, or unresolved file exists. Inspect the files and choose one path:
git -C ~/.agents status
# Keep the rebase and resolve each reported file
git -C ~/.agents add PATH_TO_RESOLVED_FILE
git -C ~/.agents rebase --continue
~/.local/bin/stackenv-skills-sync
# Or return to the exact state before the pull
git -C ~/.agents rebase --abort