Help us improve
Share bugs, ideas, or general feedback.
From session-backup
גיבוי יומי של תיקיות הסקילס, המשימות המתוזמנות והקונפיגורציות לגוגל דרייב
npx claudepluginhub msapps-mobile/claude-plugins --plugin session-backupHow this skill is triggered — by the user, by Claude, or both
Slash command
/session-backup:session-backupThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Back up all Cowork-related folders to Google Drive.
Uploads files to Google Drive via Google Apps Script web app using base64 and curl. Triggers on 'upload/save to Drive' phrases (English/Hebrew) or proactively for workflow outputs. Supports folders.
Unified CLI for Google Workspace: manage Drive files, send/read Gmail, query/edit Sheets/Docs, handle Calendar/Chat/Admin via terminal or AI agents.
Enables OAuth-based Google Workspace operations: Drive file uploads/searches/folder management, Gmail email searches, Calendar meeting queries, Docs creation.
Share bugs, ideas, or general feedback.
Back up all Cowork-related folders to Google Drive.
Session path note: Cowork VM session names change every run. Do NOT hardcode a session name like
loving-gracious-allen. Instead, discover the active session dynamically (Step 1).
import json, os, glob, sys
# Discover the active session that has the credentials file
candidates = glob.glob('/sessions/*/mnt/Claude/credentials/cowork-gdrive-config.json')
if not candidates:
print('STATUS:NOT_FOUND')
sys.exit(1)
cfg_path = candidates[0]
session_mnt = cfg_path.replace('/Claude/credentials/cowork-gdrive-config.json', '') # e.g. /sessions/abc-xyz/mnt
with open(cfg_path) as f:
cfg = json.load(f)
print('STATUS:OK')
print(f'SESSION_MNT:{session_mnt}')
print(f'URL:{cfg["url"]}')
print(f'APIKEY:{cfg["apiKey"]}')
If STATUS is NOT_FOUND, stop and report:
Config file missing. On your Mac, run once:
cp ~/.cowork-gdrive-config.json ~/Documents/Claude/credentials/cowork-gdrive-config.json
Delete temporary debug screenshots from the Claude docs folder (top level only).
Why Desktop Commander? The VM filesystem mount is read-only for deletes — use Desktop Commander to run the delete natively on the Mac.
Use mcp__Desktop_Commander__start_process with:
command: find ~/Documents/Claude -maxdepth 1 -type f \( -name '*.png' -o -name '*.jpg' -o -name '*.jpeg' -o -name '*.gif' \) -delete && echo 'Screenshots flushed'
timeout_ms: 15000
(Failure is non-fatal — continue anyway.)
All zips must exclude: */node_modules/*, */.git/*, */.next/*, *.pyc, *.png, *.jpg, *.jpeg, *.gif, */package-lock.json
Use SESSION_MNT from Step 1 for all paths.
mkdir -p /tmp/cowork-backup
cd '{SESSION_MNT}/.claude' && zip -r /tmp/cowork-backup/skills-backup.zip skills/ \
-x '*/node_modules/*' -x '*/.git/*' -x '*/.next/*' -x '*.pyc' \
-x '*.png' -x '*.jpg' -x '*.jpeg' -x '*.gif' -x '*/package-lock.json' \
2>&1 | tail -2 && ls -lh /tmp/cowork-backup/skills-backup.zip
First try as ONE zip. If under 30MB, upload as one file. If over 30MB, split into 3 parts.
cd '{SESSION_MNT}' && zip -r /tmp/cowork-backup/claude-docs-backup.zip Claude/ \
-x '*/node_modules/*' -x '*/.git/*' -x '*/.next/*' -x '*.pyc' \
-x '*.png' -x '*.jpg' -x '*.jpeg' -x '*.gif' -x '*/package-lock.json' \
2>&1 | tail -1 && ls -lh /tmp/cowork-backup/claude-docs-backup.zip
If the zip is over 30MB, delete it and split instead:
Claude/gcal-mcp/ → claude-docs-gcalmcp.zipEach part uses the same exclusion flags.
The .remote-plugins folder is mounted read-only at {SESSION_MNT}/.remote-plugins — zip it directly from the VM:
cd '{SESSION_MNT}' && zip -r /tmp/cowork-backup/plugins-backup.zip .remote-plugins/ \
-x '*.png' -x '*.jpg' -x '*.jpeg' -x '*.gif' \
2>&1 | tail -1 && ls -lh /tmp/cowork-backup/plugins-backup.zip
mkdir -p /tmp/cowork-backup/configs
cp '{SESSION_MNT}/Claude/credentials/cowork-gdrive-config.json' /tmp/cowork-backup/configs/
cd /tmp/cowork-backup && zip -r /tmp/cowork-backup/configs-backup.zip configs/ && echo done
Why Desktop Commander? The Cowork VM proxy blocks outbound connections to
script.google.com. Uploads must run on the Mac side. We do this by:
- Copying zip files to the mounted Claude folder (which is the Mac's
~/Documents/Claude/)- Writing a Python upload script there
- Using Desktop Commander's
start_processto run it natively on the Mac
Why the two-step POST→GET? Google Apps Script returns a 302 redirect to
script.googleusercontent.com. curl's-Lflag converts POST→GET for 302s, but the redirect endpoint still returns HTML. The fix: POST without following redirect, captureLocationheader, then GET that URL directly.
import shutil, os
session_mnt = 'SESSION_MNT_FROM_STEP1'
staging = f'{session_mnt}/Claude/.backup-tmp'
os.makedirs(staging, exist_ok=True)
backup_dir = '/tmp/cowork-backup'
for fname in os.listdir(backup_dir):
if fname.endswith('.zip'):
shutil.copy(os.path.join(backup_dir, fname), os.path.join(staging, fname))
print(f'Staged to: {staging}')
Write this Python script to {staging}/do_upload.py (substitute real url and api_key values):
import base64, json, subprocess, os
url = 'URL_FROM_STEP1'
api_key = cfg["apiKey"] # loaded from ~/.cowork-gdrive-config.json
staging = os.path.expanduser('~/Documents/Claude/.backup-tmp')
for fname in sorted(os.listdir(staging)):
if not fname.endswith('.zip'):
continue
fpath = os.path.join(staging, fname)
size_kb = os.path.getsize(fpath) // 1024
print(f'Uploading {fname} ({size_kb}KB)...')
with open(fpath, 'rb') as f:
b64 = base64.b64encode(f.read()).decode()
payload = {
'fileName': fname,
'content': b64,
'mimeType': 'application/zip',
'apiKey': api_key,
'folderPath': 'Cowork-Backups',
'replaceExisting': True
}
payload_path = '/tmp/upload_payload.json'
with open(payload_path, 'w') as f:
json.dump(payload, f)
# Step 1: POST without following redirect — capture Location header
r1 = subprocess.run(
['curl', '-s', '-D', '-', '--max-redirs', '0',
'-X', 'POST', url,
'-H', 'Content-Type: application/json',
'--data', f'@{payload_path}',
'--max-time', '120'],
capture_output=True, text=True
)
if os.path.exists(payload_path):
os.remove(payload_path)
# Extract Location header from response headers
location = None
for line in r1.stdout.splitlines():
if line.lower().startswith('location:'):
location = line.split(':', 1)[1].strip()
break
if not location:
print(f'ERROR: No redirect location for {fname}')
print(r1.stdout[:300])
continue
# Step 2: GET the redirect URL to retrieve the JSON response
r2 = subprocess.run(
['curl', '-s', location, '--max-time', '30'],
capture_output=True, text=True
)
print(f'{fname}: {r2.stdout[:300]}')
print('Upload complete.')
Start the process (output will be slow — capture to file for reliability):
command: python3 ~/Documents/Claude/.backup-tmp/do_upload.py > ~/Documents/Claude/.backup-tmp/upload_result.txt 2>&1; echo "EXIT:$?" >> ~/Documents/Claude/.backup-tmp/upload_result.txt
timeout_ms: 15000
Then poll read_process_output until ✅ Process completed appears (may take 60–180s for large files).
Finally, read the result:
command: cat ~/Documents/Claude/.backup-tmp/upload_result.txt
timeout_ms: 10000
If any upload fails with 413: split that zip into smaller parts and retry.
rm -rf /tmp/cowork-backup /tmp/upload_payload.json
Also remove the staging area from the mounted folder via Desktop Commander:
command: rm -rf ~/Documents/Claude/.backup-tmp && echo 'Mac staging cleaned'
timeout_ms: 10000
List all uploaded files with their Drive links (from the JSON responses). Note which components were backed up and any that were skipped.
replaceExisting: true so each run overwrites the previous backupfolderPath must be Cowork-Backups*/node_modules/*, */.git/*, */.next/*, *.pyc, *.png, *.jpg, *.jpeg, *.gif, */package-lock.json{SESSION_MNT}/.remote-plugins (read-only) — include in backup as plugins-backup.zip-L alone lands on wrong pagescript.google.com