Для: Desktop Claude (Windows Cowork sandbox)
Частота: щодня о 20:30 UTC (23:30 Kyiv). Можна викликати частіше вручну.
Встановив: VPS Claude (31.131.26.203, Opus 4.7), 2026-04-23
Триггер: Windows Task Scheduler → PowerShell → Claude Code (або /loop daily / /schedule skill)
Очікуваний час виконання: 10-30 сек при нормі, до 60 сек при cold start.
Щодня автоматично:
~/.claude/projects/-/memory/ з VPS у папку /root/.claude/projects/-/memory/desktop-memory/snapshot/.daily-sync ping у VPS inbox.Перевір що виконано:
(Get-StoredCredential -Target 'AG-VPS-SSH-Key-Claude-Desktop') повертає credential (тобто приватний ключ вже в Windows Cred Manager)._ssh-bootstrap-DELETE-AFTER-IMPORT/ видалена.ssh root@31.131.26.203 hostname з розшифрованим ключем повертає ag-dev-hub.Якщо будь-який пункт не виконано — зупинись і скажи Сергію. Не розв'язуй ініціально-налаштувальні задачі у щоденному cron'і.
$keyPath = "$env:USERPROFILE\.ssh\id_ed25519"
if (!(Test-Path $keyPath)) {
# sandbox ефемерний — restore з Cred Manager
& "$env:USERPROFILE\AG DEV HUB\restore-to-sandbox.ps1"
Copy-Item "$env:USERPROFILE\.sandbox-ssh\id_ed25519" $keyPath
icacls $keyPath /inheritance:r /grant:r "$($env:USERNAME):(R)" # chmod 600 еквівалент
}
$memDir = "$env:USERPROFILE\.claude\projects\-\memory"
$localHash = Get-ChildItem -Recurse -File $memDir `
| Where-Object { $_.FullName -notmatch '\\sessions\\' -and $_.Name -ne '.last-integrated' } `
| Get-FileHash -Algorithm MD5 `
| ForEach-Object { $_.Hash } `
| Sort-Object `
| Get-FileHash -InputObject { $_ } -Algorithm MD5 # агрегований hash
$hashFile = "$env:LOCALAPPDATA\ag-vps-memory-sync.lasthash"
$lastHash = if (Test-Path $hashFile) { Get-Content $hashFile } else { '' }
(Якщо PS-lines вище заскладні — просто порівняй (Get-ChildItem -Recurse $memDir | Measure-Object -Property Length -Sum).Sum + count як швидкий proxy. Точний hash — nice-to-have, не обов'язково.)
rsync -az --delete \
--exclude='.git/' \
--exclude='sessions/' \
--exclude='inbox-from-desktop/' \
--exclude='desktop-memory/' \
~/.claude/projects/-/memory/ \
root@31.131.26.203:/root/.claude/projects/-/memory/desktop-memory/snapshot/
--exclude sessions/ — сесійні логи специфічні середовищу, librarian'у не потрібні--exclude inbox-from-desktop/ desktop-memory/ — щоб не зациклитись (у тебе локально може бути дзеркало через ag-wiki)-z — compress (над мережею вигідно)--delete — віддзеркалюй стан, видаляй на VPS те що видалив локальноЗапам'ятай exit code ($LASTEXITCODE).
TS=$(date -u +%Y-%m-%dT%H%M%SZ)
SUMMARY=$(cat << EOF
---
from: Desktop Claude
date: $TS
topic: Daily memory sync
priority: low
auto: true
---
# Daily sync — $TS
**Результат:** rsync OK (exit=0). Snapshot оновлено у desktop-memory/snapshot/.
## Що нове з минулого разу
- <якщо можеш — короткий список змінених/доданих файлів з hash-дельти>
- <або просто "hash змінився, деталі у snapshot/">
## Що стабільно (не міняється часто)
- MEMORY.md, MEMORY-PERSONAL.md — без змін
## Інтеграційні запити до VPS librarian (якщо є)
- <нічого, або конкретна річ яку Desktop хоче, щоб VPS оновив у спільній memory>
EOF
)
echo "$SUMMARY" | ssh root@31.131.26.203 \
"cat > /root/.claude/projects/-/memory/inbox-from-desktop/${TS}-daily-sync.md"
Не кидаємо ping якщо:
Set-Content $hashFile $localHash
Add-Content "$env:LOCALAPPDATA\ag-vps-memory-sync.log" `
"$(Get-Date -Format 'yyyy-MM-dd HH:mm:ssZ') | exit=$rsyncExit | bytes=<size> | ping_sent=$pingSent"
Якщо rsync exit != 0 АБО ssh handshake провалився:
if ($rsyncExit -ne 0 -or $sshExit -ne 0) {
Add-Content "$env:LOCALAPPDATA\ag-vps-memory-sync.log" `
"$(Get-Date) | ERROR | rsync=$rsyncExit ssh=$sshExit | <stderr>"
# 3 невдачі поспіль → notify Сергія через Cowork
$fails = (Get-Content "$env:LOCALAPPDATA\ag-vps-memory-sync.log" -Tail 3) `
| Where-Object { $_ -match 'ERROR' } | Measure-Object | % Count
if ($fails -ge 3) {
Write-Host "⚠️ Memory sync fail 3 рази поспіль. Перевір VPS або SSH ключ."
# показати Сергію у Cowork-чаті
}
}
НЕ ламай свою роботу через сетьові помилки VPS. Просто логуй, ретраюй завтра.
~/.ssh/id_ed25519 присутній{timestamp}-daily-sync.mdag-vps-memory-sync.log2026-04-24 20:30:14Z | exit=0 | bytes=847233 | ping_sent=true | hash=a3f...→b91...
2026-04-25 20:30:11Z | exit=0 | bytes=847233 | ping_sent=false | hash=b91...==b91...
2026-04-26 20:30:12Z | ERROR | rsync=23 | Connection timeout
При моєму SessionStart hook:
📬 Inbox від Desktop: 1 непрочитаних (2026-04-24T203014Z-daily-sync.md) у /root/.claude/projects/-/memory/inbox-from-desktop/🗂 Desktop memory: N нових/змінених файлів з останньої інтеграціїЯ (на наступній моїй сесії) читаю daily ping, роблю librarian pass (порівняння snapshot ↔ spillna memory), вношу корисне у MEMORY.md / decisions_log / project_*.md. Про цікаві знахідки звітую Сергію.
agents_inventory.md.Get-Content | Select -Last 500 | Set-Content).193.93.78.10, whitelisted на VPS у fail2ban + from= обмеження в authorized_keys):
→ curl -s https://api.ipify.org при першому failed connect, якщо інший — скажи Сергію, я оновлю обмеження.$action = New-ScheduledTaskAction -Execute 'pwsh.exe' `
-Argument '-NoProfile -File "C:\Users\<user>\AG DEV HUB\daily-memory-sync.ps1"'
$trigger = New-ScheduledTaskTrigger -Daily -At '23:30' # 20:30 UTC
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable `
-DontStopIfGoingOnBatteries -RestartCount 2 -RestartInterval (New-TimeSpan -Minutes 10)
Register-ScheduledTask -TaskName "AG-VPS-Daily-Memory-Sync" `
-Action $action -Trigger $trigger -Settings $settings `
-Description "Daily rsync of Desktop Claude memory to VPS; by design auto-skips if nothing changed."
Якщо Desktop Claude вже використовує /loop / /schedule skill у Cowork — завдання простіше:
/schedule "Daily memory sync" "30 20 * * *" "execute daily-memory-sync routine from /srv/reports/task-for-desktop-daily-memory-sync-2026-04-23.md"
Працюємо. Після першого успішного daily-sync я скажу Сергію у Telegram. 🔁
— VPS Claude