Skip to main content

Mirror & sync Claude Code project markdown across machines + cloud backends (Google Drive, Dropbox, OneDrive, WebDAV, SFTP, S3-compatible, FTP/FTPS) with snapshots and Tier 2 multi-backend

Project description

claude-mirror

Tests PyPI Python License Discussions

Mirror your project files across machines and cloud backends — with multi-cloud redundancy, time-travel disaster recovery, and real-time collaboration signals.

Built originally for Claude Code projects (where most context lives in markdown), but the file-pattern glob is configurable to sync any file type. The default file_patterns: ["**/*.md"] keeps just Claude context files in sync; set it to ["**/*"] to mirror the entire project tree, or any other glob (e.g. ["**/*.py", "**/*.md"]) to scope what gets synced. Cross-tool agent-context sync is a first-class scenario: a single AGENTS.md file (the cross-IDE convention read by Claude Code, Cursor, Codex, and Antigravity) can be kept in lockstep across every machine via the worked sample profile at docs/profiles/agents-md.yaml — see docs/scenarios.md — Scenario I.

Why use it

  • Multi-cloud redundancy. Push to multiple backends in parallel (e.g. Google Drive + Dropbox + OneDrive). Any single provider's outage, account suspension, or quota cap never costs you data. Per-mirror retry queues mean a transient failure on one backend never blocks the rest.
  • Time-travel disaster recovery. Every push and sync auto-creates a snapshot. claude-mirror history PATH shows every version of any file across snapshots; claude-mirror restore rolls a single file or the whole project back to any past timestamp. Two storage formats: content-addressed blobs (identical content across snapshots stored once) or full per-snapshot copies.
  • Near-real-time collaboration. Pub/Sub (Drive), long-poll (Dropbox), or polling (OneDrive / WebDAV / SFTP / FTP / S3 / SMB) push remote changes to other machines within seconds. Optional per-project Slack webhooks pipe events to a team channel.
  • No-loss conflict resolution. When both sides change a file, interactive choice: keep local, keep remote, open $EDITOR for manual merge, or skip. No silent overwrites.

Supported backends: Google Drive, Dropbox, Microsoft OneDrive, any WebDAV server (Nextcloud, OwnCloud, Apache mod_dav, Synology/QNAP NAS, Box.com, etc.), any SFTP/SSH-accessible server (VPS, NAS, shared hosting, self-hosted Linux), any S3-compatible object store (AWS S3, Cloudflare R2, Backblaze B2, Wasabi, MinIO, Tigris, IDrive E2, Linode Object Storage, DigitalOcean Spaces, Storj, Hetzner Storage Box, …), any SMB/CIFS share (Windows file shares, Synology / QNAP / TrueNAS NAS devices, macOS Sharing, generic Samba — SMB2/3 only with optional per-message encryption), and FTP / FTPS (legacy shared-hosting providers — cPanel, DirectAdmin, NAS — via Python's stdlib, no new dependency; use FTPS or the SFTP backend wherever possible because plain FTP transmits credentials in cleartext). Each project picks its own primary backend independently — different projects on the same machine can use different backends.

Quality gates: Every commit and pull request runs 1345 automated tests on Linux and Windows on Python 3.11, 3.12, 3.13, and 3.14 in parallel via GitHub Actions, plus a separate mypy --strict static-type-checking job on every commit and PR. Coverage spans the 3-way diff sync core, both snapshot formats (with named tags + messages + tag-protected pruning), path-traversal safety, conflict resolution, auth flows, eight backends with HTTP-level / SSH-level / S3-level / SMB-level mocking (Google Drive, Dropbox, OneDrive, WebDAV, SFTP, FTP/FTPS, S3-compatible, and SMB/CIFS — both S3 and SMB new in v0.5.65), the read-only FUSE mount engine and its 5 variants (snapshot / live / per-mirror / all-snapshots-stacked / time-travel), the four shipping CLI introspection surfaces (tree / ncdu / stats / verify), the notifier inbox under concurrent writers (cross-platform locking via fcntl.flock on POSIX + msvcrt.locking on Windows), the Discord / Teams / Generic webhook notifiers, the watcher daemon's cross-platform sentinel-file hot-reload, the --json output mode for status / history / inbox / log / snapshots / health / stats / verify, the .claude_mirror_ignore parser, the bandwidth-throttle token-bucket integration across every backend, the credentials-profile resolution + merge-precedence rule (project YAML wins over profile defaults), the transfer-progress callback wiring, the global rate-limit BackoffCoordinator, the non-interactive sync --no-prompt --strategy flow for cron / unattended use, dynamic --backend shell completion (zsh / bash / fish / powershell), multi-channel notification routing per project, per-event webhook templating, the Drive BYO wizard, the Drive Pub/Sub auto-setup logic, and the deep doctor --backend checks for all eight backends. CI must be green before any PR can merge. See CONTRIBUTING.md for the test conventions and how to run them locally.


How it works

  • Files matching configured patterns (default: **/*.md) are synced to a shared cloud folder
  • A local manifest tracks file hashes to detect what changed since the last sync
  • When you push, collaborators are notified in near-real-time:
    • Google Drive — Cloud Pub/Sub streaming (sub-second latency)
    • Dropboxfiles/list_folder/longpoll (seconds latency)
    • OneDrive / WebDAV / SFTP / FTP / S3 / SMB — periodic polling (default 30s, configurable)
  • Conflicts (both sides changed) are resolved interactively: keep local, keep remote, or open in $EDITOR — see docs/conflict-resolution.md
  • A snapshot is saved after every push or sync, enabling point-in-time recovery — see docs/admin.md
  • Multi-backend mirroring (Tier 2) — push to multiple backends simultaneously (e.g. Drive + SFTP), with per-backend retry, classified error handling, and snapshot mirroring — see docs/admin.md and docs/scenarios.md
  • Optional Slack notifications on push/pull/sync/delete (per-project, opt-in, webhook-based)

Supported storage backends

Each backend ships in the base install — pipx install claude-mirror enables every backend (eight as of v0.5.65, when S3 + SMB joined). Per-backend setup walkthroughs live under docs/backends/:

Backend Latency Setup Reference
Google Drive sub-second (Pub/Sub gRPC) OAuth2 + GCP project (Drive API + Pub/Sub API) docs/backends/google-drive.md
Dropbox seconds (long-poll) OAuth2 PKCE + Dropbox app docs/backends/dropbox.md
OneDrive up to poll_interval (default 30s) Device-code OAuth2 + Azure AD app docs/backends/onedrive.md
WebDAV up to poll_interval (default 30s) URL + username + app password (Nextcloud, OwnCloud, NAS, Apache mod_dav, Box, Synology, QNAP, ...) docs/backends/webdav.md
SFTP up to poll_interval (default 30s) SSH key (preferred) or password — any OpenSSH-accessible server docs/backends/sftp.md
S3-compatible up to poll_interval (default 30s) Access key ID + secret access key + bucket; endpoint URL for non-AWS services (R2, B2, MinIO, Wasabi, Spaces, Storj, Tigris, IDrive E2, Linode, Hetzner, …) docs/backends/s3.md
FTP / FTPS up to poll_interval (default 30s) Host + username + password — cPanel / DirectAdmin / shared hosting / NAS. Plain FTP supported but use FTPS or the SFTP backend wherever possible docs/backends/ftp.md
SMB/CIFS up to poll_interval (default 30s) Server + share + username + password (or domain user) — Windows file shares, Synology, QNAP, TrueNAS, macOS Sharing docs/backends/smb.md

Prerequisites

  • Python 3.11 or later

Plus, depending on the backend you choose:

Backend Requires
Google Drive A Google account and a Google Cloud project (free tier is fine)
Dropbox A Dropbox account and a Dropbox app (free, created at dropbox.com/developers)
OneDrive A Microsoft account and an Azure AD app registration (free, created at portal.azure.com)
WebDAV A WebDAV server URL + username + app password (e.g. Nextcloud / OwnCloud / NAS / Apache mod_dav)
SFTP An SSH-accessible server (VPS / NAS / shared hosting / self-hosted Linux) — SSH key recommended, password fallback OK on LAN
S3-compatible An S3-compatible bucket (AWS / R2 / B2 / Wasabi / MinIO / Spaces / Storj / Tigris / IDrive E2 / Linode / Hetzner …) — access key ID + secret access key + bucket name
FTP / FTPS An FTP-accessible server (cPanel / DirectAdmin / legacy shared hosting / NAS). FTPS strongly recommended; plain FTP works but transmits credentials in cleartext — LAN-only
SMB/CIFS An SMB2/3 server — Windows file share, Synology / QNAP / TrueNAS NAS, macOS Sharing-enabled folder, or any Samba host. SMBv1 is refused (security)

Install

Recommended: pipx from PyPI

pipx installs into an isolated environment and puts claude-mirror on your PATH permanently — no venv activation required:

brew install pipx   # macOS; see https://pipx.pypa.io for other platforms
pipx ensurepath     # adds ~/.local/bin to PATH if not already there

pipx install claude-mirror

Every backend ships in this single install — no per-backend extras needed.

Verify:

claude-mirror --version

Alternative: pip in a venv

python3 -m venv ~/.venvs/claude-mirror
source ~/.venvs/claude-mirror/bin/activate
pip install claude-mirror

You must activate the venv in every new shell. The Claude Code skill (which runs in a non-interactive shell) won't be able to call claude-mirror from a venv — use pipx for that case.

Developer install (editable)

git clone https://github.com/alessiobravi/claude-mirror.git
cd claude-mirror
pipx install -e .

Install components (skill, hook, watcher, completion)

Once claude-mirror is on your PATH, run the installer to set up the Claude Code skill, notification hook, background watcher, and shell tab-completion in one step:

claude-mirror-install

You will be prompted to confirm each component before anything is written. To remove later:

claude-mirror-install --uninstall

After install, claude-mirror <TAB> lists all commands; claude-mirror init --backend <TAB> shows the eight valid backends. Tab-completion is supported on zsh, bash, fish, and PowerShell — the installer auto-detects your shell from $SHELL (or defaults to PowerShell on Windows). To install PowerShell completion manually:

claude-mirror completion powershell | Out-File -Encoding utf8 -Append $PROFILE.CurrentUserAllHosts

Verify desktop notifications with claude-mirror test-notify.

Updating

pipx upgrade claude-mirror              # PyPI installs
claude-mirror update --apply            # auto-detects PyPI vs editable; uses pipx upgrade or git pull + pipx install -e . --force

Optional: read-only FUSE mount support

claude-mirror mount exposes any snapshot — or the live current state of any backend — as a real read-only filesystem path. Useful for grep -r, diff, or opening a snapshot in your editor without committing to a full restore. See the Browsing snapshots without downloading cheatsheet below and the full reference in docs/scenarios.md — Scenario J.

The Python bindings ship in the base install (pipx install claude-mirror) — no extras flag needed. You only need to install the platform's kernel layer once per machine:

Platform Install
macOS brew install --cask macfuse
Linux already kernel-resident on every modern distro (in-tree libfuse)
Windows install WinFsp

When fusepy is missing, claude-mirror mount exits non-zero and prints the install hint above for the host platform.


Documentation index

The trimmed README covers install, your first project, daily-usage cheatsheet, notifications, and troubleshooting. Everything else lives under docs/:

Backends (per-backend setup, config fields, troubleshooting):

Operations & admin:

  • docs/admin.md — snapshots, retention, gc / prune / forget, doctor, watcher service, multi-backend Tier 2 setup, auto-start
  • docs/cli-reference.md — every command, every flag
  • docs/conflict-resolution.md — interactive conflict prompts, $EDITOR merge, three-way diff
  • docs/faq.md — frequently asked questions across auth, sync, snapshots, notifications, performance, and migration; 30-second answers with links into the depth-docs
  • docs/profiles.md — credentials profiles: factor credentials_file / token_file / app keys out of every project YAML

Topology guides (pick the one that matches your situation):

  • docs/scenarios.md — nine deployment topologies, end to end:
    • A. Standalone — local ↔ 1 backend
    • B. Personal multi-machine — local ⇄ 1 backend ⇄ local'
    • C. Multi-user collaboration — Alice ⇄ shared backend ⇄ Bob
    • D. Multi-backend redundancy — local → primary + N mirrors
    • F. Selective sync — custom file_patterns + exclusions
    • G. Multi-user + multi-backend (production-realistic) — shared primary + shared mirror, full Alice/Bob YAMLs and command-by-command transcript
    • H. Multi-project enterprise — many configs in ~/.config/claude_mirror/
    • I. Cross-tool AGENTS.md sync — single AGENTS.md shared by Claude Code / Cursor / Codex / Antigravity, narrow pattern set via the agents-md sample profile
    • J. Browse / grep / diff snapshots without restoring — read-only FUSE mount engine with five variants (snapshot / live / per-mirror / all-snapshots-stacked / time-travel)

Your first project

The two examples below cover the most common path (Google Drive — most users) and the simplest path (SFTP — zero cloud setup, works against any SSH-accessible server). Dropbox / OneDrive / WebDAV walk-throughs follow the same shape; see the per-backend docs above.

Option 1 — Google Drive

cd /path/to/your/claude/project
claude-mirror init --wizard --backend googledrive

The wizard asks for the Drive folder ID, GCP project ID, Pub/Sub topic ID, and credentials file path. Press Enter to accept defaults. Sample run:

Storage backend [googledrive]:
Project directory [/Users/alice/work/myproject]:
Credentials file [~/.config/claude_mirror/credentials.json]: ~/.config/claude_mirror/work-credentials.json

Drive folder ID: 1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OhBlt
GCP project ID: my-gcp-project
Pub/Sub topic ID [claude-mirror-myproject]:
Token file [~/.config/claude_mirror/work-token.json]:
File patterns [**/*.md]:

Save this configuration? [Y/n]:

Then authenticate (opens a browser window):

claude-mirror auth

Detailed setup of the GCP project, OAuth consent screen, Drive folder, and Pub/Sub topic — including how to invite collaborators — lives in docs/backends/google-drive.md.

Option 2 — SFTP

cd /path/to/your/claude/project
claude-mirror init --wizard --backend sftp

The wizard asks for hostname, username, root path, and SSH key (or password). Sample run:

Storage backend [sftp]:
Project directory [/Users/alice/work/myproject]:

SFTP host: backup.example.com
SFTP username [alice]:
Root path on server: /home/alice/claude-mirror/myproject
SSH key file [~/.ssh/id_ed25519]:
Poll interval (seconds) [30]:
File patterns [**/*.md]:

Save this configuration? [Y/n]:

No interactive auth step — the wizard validates the SSH connection and host fingerprint immediately. Set up the remote root path and SSH key permissions ahead of time; full walkthrough at docs/backends/sftp.md.

After init: push your first batch

claude-mirror status     # see what's local-ahead vs in-sync
claude-mirror push       # upload the local-ahead files; creates a snapshot

Each collaborator runs claude-mirror auth (or init --wizard for SFTP-key setups) on their own machine with their own credentials. Tokens are unique per machine, never shared.

Cloning to a new machine

When the project already exists on the remote (e.g. you set it up on your laptop yesterday and you're now on your desktop, or a teammate is joining a shared project), claude-mirror clone does init + auth + the first pull in one shot — instead of three separate commands:

claude-mirror clone --backend googledrive \
                    --project ~/projects/myproject \
                    --drive-folder-id <FOLDER_ID> \
                    --gcp-project-id <GCP_ID> \
                    --pubsub-topic-id <TOPIC>

Or interactively — same wizard prompts as init --wizard:

claude-mirror clone --wizard --backend googledrive --project ~/projects/myproject

If auth fails, the partial YAML is rolled back automatically so the next attempt starts clean. Use --no-pull when this machine is the one seeding a brand-new remote (config + token in place, nothing yet to download). Full flag list and rollback semantics in docs/cli-reference.md — clone; the multi-machine workflow this command serves is docs/scenarios.md — Scenario B.

Multiple projects on the same machine

Repeat init --wizard once per project. Every project gets its own config file at ~/.config/claude_mirror/<project>.yaml. Different projects on the same machine can use different backends — Drive for one, SFTP for another, WebDAV for a third. All commands auto-detect the right config from the current working directory.

Multiple accounts and mixed backends

When several projects share the same account (one Google account → 5 projects, one Dropbox app → 3 projects), use credentials profiles to factor the duplicated credential fields out of the per-project YAMLs:

claude-mirror profile create work --backend googledrive    # one-time scaffold
cd ~/projects/research
claude-mirror --profile work init --wizard                  # inherits credentials, prompts only for project-specific fields
cd ~/projects/strategy
claude-mirror --profile work init --wizard                  # same profile, different folder/topic

The global --profile NAME flag (since v0.5.49) goes BEFORE the subcommand. After init, each project YAML carries profile: work at the top so subsequent push/pull/sync commands pick the profile up automatically. Project YAML values still win over profile defaults, so any one project can override a single field as a per-project escape hatch. See docs/profiles.md for the full walkthrough.

For team-shared / multi-backend / multi-user setups, see docs/scenarios.md.


Daily usage cheatsheet

All commands work identically regardless of backend. Run from inside the project directory; the right config is auto-detected.

claude-mirror status                    # 3-way diff: local / remote / manifest
claude-mirror status --short            # one-line summary, no table
claude-mirror status --by-backend       # per-file × per-backend live state (Tier 2 setups)
claude-mirror push                      # upload local-ahead files; snapshot
claude-mirror push file1.md file2.md    # push specific files only
claude-mirror push --dry-run            # preview the upload plan; no network writes, no local writes
claude-mirror pull                      # download remote-ahead files
claude-mirror pull --output DIR         # preview to DIR without touching local files
claude-mirror pull --dry-run            # preview the download plan; no network reads, no local writes
claude-mirror diff path/to/file.md      # colourised unified diff (remote → local)
claude-mirror tree                      # tree(1)-style remote file listing with sizes
claude-mirror delete file.md --dry-run  # preview what a real delete would remove; no writes
claude-mirror delete file.md --local    # remove from remote AND local disk
claude-mirror sync                      # full bidirectional with conflict prompts
claude-mirror sync --no-prompt --strategy keep-local   # cron-friendly: auto-resolve all conflicts
claude-mirror conflict list             # AGENT-MERGE: every pending conflict envelope (skill-driven)
claude-mirror conflict show <path> --format markers   # fetch a conflict in 3-way <<<<<<< / ======= / >>>>>>> markers
claude-mirror conflict apply <path> --merged-file <tmp>   # write the agent's merge + clear envelope + push
claude-mirror watch                     # foreground watcher; system notification on remote pushes
claude-mirror watch --once --quiet      # single polling cycle (cron-friendly: */5 * * * * ...)
claude-mirror watch-all                 # watch every config in ~/.config/claude_mirror/
claude-mirror log                       # who pushed what, when, across machines
claude-mirror log --follow              # live tail -f: stream new entries as they arrive
claude-mirror stats --since 7d          # rolled-up usage summary; --by user|machine|action|day|backend
claude-mirror inbox                     # show + clear pending notifications
claude-mirror snapshot --tag v1.0 --message "first stable"   # explicit named snapshot you can restore by name
claude-mirror restore --tag v1.0        # restore by tag instead of by timestamp
claude-mirror ncdu                      # interactive curses TUI of remote disk usage (POSIX only; arrows + Enter to navigate)
claude-mirror ncdu --non-interactive --top 10   # cron-friendly: print top-10 largest paths and exit
claude-mirror status --json | jq '.result.summary'   # script-friendly output
claude-mirror verify --strict           # end-to-end integrity audit: manifest vs remote + snapshot blobs + mount cache
claude-mirror redact .                  # pre-push secret scan: dry-run finds AKIA / ghp_ / sk-ant- / etc; --apply --yes scrubs in place

Per-project gitignore-style exclusions: drop a .claude_mirror_ignore at the project root for **/!negation//anchored/dir/ rules that complement YAML exclude_patterns. Full syntax in docs/admin.md.

Snapshot, restore, and admin commands (history, snapshots, restore, prune, gc, forget, doctor, seed-mirror, migrate-state) live in docs/cli-reference.md, with operational guidance in docs/admin.md.

Conflict resolution flow (interactive keep local / keep remote / merge / skip prompt) is documented in docs/conflict-resolution.md.

Browsing snapshots without downloading

Read-only FUSE mount surface — grep -r, diff, or open a snapshot in your editor without running restore. Ships in the base install since v0.5.61; you only need to install the platform's kernel layer separately (see the Mount install section above).

mkdir /tmp/snap
claude-mirror mount --tag pre-refactor /tmp/snap          # one frozen snapshot
claude-mirror mount --snapshot 2026-04-15T10-30-00Z /tmp/snap   # one snapshot by timestamp
claude-mirror mount --as-of 2026-04-15 /tmp/april15       # last snapshot on or before DATE
claude-mirror mount --all-snapshots /tmp/all-history      # every snapshot under per-timestamp dirs
claude-mirror mount --live /tmp/drive-now                 # current state of primary backend
claude-mirror mount --live --backend dropbox /tmp/dbx     # current state of one Tier 2 mirror
grep -r 'TODO' /tmp/snap                                   # works like any read-only filesystem
diff /tmp/snap/CLAUDE.md ~/projects/myproject/CLAUDE.md
claude-mirror umount /tmp/snap                             # cross-platform unmount wrapper

Read-only by design — writes return EROFS. Blob bodies are content-addressed and cached forever at $XDG_CACHE_HOME/claude-mirror/blobs/; the cache survives unmount/remount. Default cache cap 500MB, configurable via --cache-mb N. Full recipe with pitfalls in docs/scenarios.md — Scenario J.


Shell prompt integration

Inspired by git's __git_ps1: claude-mirror prompt emits a short, network-free, sub-50ms status snippet you can drop into your shell prompt to see sync state at a glance on every command. The output is one of (in sync), ↑N (N files locally ahead), ~N (N pending_retry conflicts), ? (no manifest yet), (error reading state) — or empty with --quiet-when-clean. Pass --format ascii for OK / +N / ~N / ? / ! if your terminal struggles with UTF-8, or --format json for a parseable dict. Full reference: docs/cli-reference.md.

The command is silent on every error path and ALWAYS exits 0 — a non-zero exit would tear your prompt on every command after a corrupt manifest or stale config. Errors surface as a single stderr line plus a warning glyph on stdout. Drop into a non-claude-mirror directory and the command exits with empty stdout, so embedding it unconditionally is safe.

# bash (PS1)
PS1='\u@\h:\w$(claude-mirror prompt --quiet-when-clean --prefix " ")\$ '

# zsh (PROMPT)
setopt PROMPT_SUBST
PROMPT='%n@%m:%~$(claude-mirror prompt --quiet-when-clean --prefix " ") %# '

# fish (function fish_prompt)
function fish_prompt
    echo -n (whoami)@(hostname):(prompt_pwd)
    set -l mirror_status (claude-mirror prompt --quiet-when-clean --prefix " ")
    test -n "$mirror_status" && echo -n "$mirror_status"
    echo -n " > "
end
# starship (~/.config/starship.toml)
[custom.claude_mirror]
command = "claude-mirror prompt --quiet-when-clean"
when = "claude-mirror find-config"
format = "[$output]($style)"
style = "yellow"

Performance contract: cold cache is ~6-8 ms of in-process work on a 500-file project, warm cache ~3-4 ms. The path consults a tiny cache file at .claude_mirror_prompt_cache.json keyed on the manifest's mtime + live file count, so repeated invocations on an unchanged project are nearly free. Above 5000 files the prompt returns a cached value or an ellipsis () rather than blocking the shell.


Claude Code skill

claude-mirror ships a skill for Claude Code that lets you run sync operations directly from your AI conversation, and surfaces remote notifications inline without leaving the editor.

How it works

Collaborator runs claude-mirror push on Machine B
  → Notification fires on the backend's channel (Pub/Sub, longpoll, or polling)
  → claude-mirror watch (running in background) receives it
  → System notification sent
  → Event written to {project}/.claude_mirror_inbox.jsonl

You type /claude-mirror in Claude Code
  → Skill auto-detects your active project via find-config
  → Reads and clears the inbox — pending notifications shown in conversation
  → Runs status — full sync state shown in conversation
  → You can push / pull / sync / restore by talking to Claude

Notifications are project-scoped — each project has its own .claude_mirror_inbox.jsonl inside the project directory. Switching projects is as simple as cd-ing to a different directory.

Setup

claude-mirror-install (run once, see Install) handles the skill, the PreToolUse hook in ~/.claude/settings.json, and the background watcher service. No further configuration needed — the skill auto-detects the active project.

The hook makes notifications appear automatically inside the Claude Code conversation: it runs silently before every tool call, calls claude-mirror inbox to print + clear any pending notifications, and stays invisible when nothing is pending. Combined with the watcher:

You are... How you get notified
Actively using Claude Code Hook fires on next tool call — notification appears inline
Idle / away from Claude Code Desktop notification from the watcher

Use it

In any Claude Code conversation, type:

/claude-mirror

Claude will detect the config for your current working directory, report any pending notifications, show the full sync status, and offer a smart-merge for any remote-ahead or conflict files (downloads remote versions to a preview dir, diffs against local + in-session edits, produces an intelligent merged result, asks before writing).

You can also ask in natural language: "push my changes", "pull the latest", "sync everything", "what's different in this file?", "show me the snapshots", "restore to 10:30 this morning", "clean up old snapshots", "what changed recently".

For background and persistent-service setup of claude-mirror watch-all, see docs/admin.md#auto-start-the-watcher.

Recommended project memory directives

For best results, add the following directives to every project's MEMORY.md (or CLAUDE.md). They instruct Claude to keep memory and working files in sync and to review changes carefully before pushing.

- **Project home:** the current working directory (wherever this project is opened from)
  — all MD files mirrored here (copy of memory)

- **MIRROR RULE:** After every file change in memory, immediately copy the changed
  file(s) to the project working directory. Both locations must always be in sync.

- **PRE-SYNC REVIEW RULE:** When one or more project markdown files are updated in
  the working directory, before any claude-mirror action:
  (1) re-read all changed files, (2) diff against current memory,
  (3) report a clear summary of what changed,
  (4) ask for explicit confirmation before writing to memory and pushing.

- **POST-PULL EVALUATION RULE:** When claude-mirror pulls remote changes (inbox
  notification or status showing drive-ahead files), immediately re-read all changed
  files, diff against current memory, produce a structured analysis of what changed
  and what should be updated in memory (new facts, stale entries, discrepancies
  across files), then ask for confirmation before writing to memory or pushing.

These rules are also built into the skill itself and apply automatically during every /claude-mirror invocation.


Messaging and communication

claude-mirror posts on every sync event (push / pull / sync / delete) to one or more chat / automation backends AND surfaces native desktop banners on the running watcher's machine. All channels are per-project, opt-in, and best-effort — a notification failure (network error, bad URL, 4xx, 5xx, missing notification daemon) is logged and silently swallowed; it will never block or fail a sync. Multiple channels can fire simultaneously on the same project.

Channel When to pick it Setup walkthrough
Slack Team chat with Slack; richest payload (rich blocks, per-backend Tier 2 status, ACTION REQUIRED on permanent failures) docs/admin.md → Slack
Discord Team chat with Discord; coloured embed cards (green = push, blue = pull / sync, red = delete) docs/admin.md → Discord
Microsoft Teams Team chat with Teams; legacy O365 connector or modern Workflows webhook (both shapes accepted) docs/admin.md → Microsoft Teams
Generic webhook Wiring into n8n / Make / Zapier / a custom dashboard; schema-stable v1 JSON envelope; optional Bearer-token / custom-header support docs/admin.md → Generic
Desktop banners Native macOS / Linux / Windows toast notifications on the machine running claude-mirror watch-all docs/admin.md → Desktop notifications

Quick verify after setting up any channel:

claude-mirror test-notify              # fires a sample event through every enabled channel

For richer routing (per-event-type or per-path-glob — e.g. send secrets/** events to a security channel and everything else to the firehose), per-event message templating with placeholder variables, and the full config-field reference (slack_enabled, slack_webhook_url, discord_*, teams_*, webhook_*, *_routes, *_template_format), see docs/admin.md → Notifications.


Update notifications

claude-mirror checks once per 24h whether a newer version exists on this project's GitHub mirror and tells you about it. The check is best-effort, offline-tolerant, and never blocks a command — it runs in a background daemon thread and only ever prints a single inline notice when the cached "latest version" is newer than the locally-installed one.

On any command launch when an update is available:

🆕 claude-mirror 0.4.1 is available (you have 0.4.0).
Update: pipx install -e . --force from your repo dir, or set CLAUDE_MIRROR_NO_UPDATE_CHECK=1 to silence.

Inside the long-running watcher daemon, the same event also fires a desktop notification — but only ONCE per new version (tracked in cache).

Manual check

claude-mirror check-update     # bypasses the 24h cache
claude-mirror update           # dry-run: shows current → latest and the command that would run
claude-mirror update --apply   # runs git pull + pipx install -e . --force (editable) or pipx upgrade (PyPI)
claude-mirror update --apply --yes   # skip the confirmation prompt

Cache and opt-out

Item Default How to change
Cache location ~/.config/claude_mirror/.update_check.json Hardcoded; safe to delete
TTL 24h claude-mirror check-update bypasses
Opt out (per-shell) (off) export CLAUDE_MIRROR_NO_UPDATE_CHECK=1
Opt out (permanent) (off) Add the export to your .zshrc / .bashrc

The check looks at PyPI first (https://pypi.org/pypi/claude-mirror/json, the most authoritative source for installability — a release isn't real until the wheel is on PyPI), falls back to the GitHub Contents API (https://api.github.com/repos/alessiobravi/claude-mirror/contents/pyproject.toml) when PyPI is unreachable, and finally to the raw CDN (https://raw.githubusercontent.com/alessiobravi/claude-mirror/main/pyproject.toml) when both are unavailable. Each fallback only runs if the prior fails. Every request includes a User-Agent: claude-mirror/<version> update-check header — no telemetry data is sent.


Troubleshooting

The most common gotchas are listed here. For backend-specific errors (Drive auth, Dropbox/OneDrive token cache, WebDAV MKCOL, SFTP host fingerprint), see the per-backend docs under docs/backends/.

RefreshError: Reauthentication is needed (Google Drive)

The OAuth refresh token has expired or been revoked. The error mentions gcloud — that's misleading; claude-mirror does not use gcloud credentials.

Fix: claude-mirror auth --config ~/.config/claude_mirror/<project>.yaml — opens a browser for fresh OAuth login. The stale token is replaced; the rest of the config is preserved.

For "auth expires every day or two" symptoms (caused by the OAuth consent screen sitting in Testing mode, or Workspace Cloud Session Control settings), see the in-depth diagnosis in docs/backends/google-drive.md.

Not authenticated. Run claude-mirror auth first.

No token file exists — auth was never run on this machine, or the token file was deleted. Fix: run claude-mirror auth.

Dropbox: "Authentication code expired" or "invalid_grant"

The one-shot authorization code printed during claude-mirror auth has a short lifetime. Re-run claude-mirror auth, complete the flow promptly, and paste the code on first try.

OneDrive: AADSTS50058 or "no cached accounts found"

The MSAL token cache is missing, corrupted, or its refresh token has been revoked. Re-run claude-mirror auth — the device-code flow rebuilds the cache.

WebDAV: 401 Unauthorized or 405/409 from MKCOL

401 — wrong credentials, or a real account password where an app password is required (Nextcloud / OwnCloud with 2FA). Generate an app password and re-run claude-mirror init (or edit webdav_username + re-run claude-mirror auth).

405/409 from MKCOL — the server refused to create the project folder. Create it manually via the server's web UI or another WebDAV client, then re-run claude-mirror push.

SFTP: host fingerprint or permission errors

See docs/backends/sftp.md for the full troubleshooting flow (~/.ssh/known_hosts updates, internal-sftp-jailed accounts, server-side sha256sum fallback).

claude-mirror doctor

For end-to-end self-test (config + creds + connectivity + backends + manifest sanity), run:

claude-mirror doctor

See docs/admin.md#doctor for the full check matrix and output interpretation.


Monitoring & alerting

For unattended monitoring (Uptime Kuma, Better Stack, Prometheus textfile-exporter, Datadog, GitHub Actions matrix health checks, ...), reach for claude-mirror health rather than doctor. Where doctor is the verbose human-readable diagnostic you run when something is broken, health is the fast structured probe a monitoring tool polls on a schedule. Both share data sources but the surface is tuned for different audiences.

claude-mirror health runs six checks (config_yaml, token_present, backend_reachable, mirrors_reachable, watcher_running, last_sync_age) and exits with one of three codes that any monitoring tool can key off:

Exit Overall Meaning
0 ok Healthy.
1 warn At least one check warned (e.g. last_sync_age between 24h and 72h).
2 fail At least one check failed. Page now.

Pass --json for a parseable envelope ({"schema": "v1", "command": "health", "generated_at": ..., "overall": ..., "checks": [...]}) instead of the Rich table; pass --no-backends to skip the network-touching probes for fast local-only checks that don't burn API quota.

Sample one-liner for cron — fire a notification on any non-zero exit so monitoring picks up both warn and fail:

*/1 * * * * /usr/local/bin/claude-mirror health --json --no-backends || /usr/local/bin/notify-monitor

See docs/cli-reference.md#health for the full check matrix, JSON envelope schema, and integration examples.


File locations

All token files are written with chmod 0600 (owner read/write only).

File Purpose
~/.config/claude_mirror/<account>-credentials.json OAuth2 client credentials for one Google account (share with team)
~/.config/claude_mirror/<account>-token.json Personal access token for Google Drive (do not share)
~/.config/claude_mirror/dropbox-<project>-token.json Personal Dropbox refresh token (do not share)
~/.config/claude_mirror/onedrive-<project>-token.json MSAL token cache for OneDrive (do not share)
~/.config/claude_mirror/webdav-<project>-token.json WebDAV credentials — URL, username, password in plaintext at 0600. Prefer an app password.
~/.config/claude_mirror/sftp-<project>-token.json SFTP host-fingerprint cache (key auth uses your ~/.ssh/; password fallback is plaintext at 0600)
~/.config/claude_mirror/ftp-<project>-token.json FTP marker file — credentials live inline in the project YAML (host / username / password / folder)
~/.config/claude_mirror/s3-<project>-token.json S3 marker file — access key + secret live inline in the project YAML, or boto3's default credential chain handles them
~/.config/claude_mirror/smb-<project>-token.json SMB marker file — credentials live inline in the project YAML (server / share / username / password / folder)
~/.config/claude_mirror/<project>.yaml Per-project config (auto-named from project directory)
{project}/.claude_mirror_manifest.json Local sync state (do not edit manually)
{project}/.claude_mirror_hash_cache.json Per-project hash cache to skip re-hashing unchanged files. Safe to delete; rebuilt on next run. Add to .gitignore.
{project}/.claude_mirror_inbox.jsonl Pending notifications for Claude Code skill (auto-cleared on read)
~/.claude/skills/claude-mirror/SKILL.md Claude Code skill definition
~/.claude/settings.json Claude Code settings (PreToolUse hook lives here)
~/Library/LaunchAgents/com.claude-mirror.watch.plist macOS: launchd agent for background watcher
~/.config/systemd/user/claude-mirror-watch.service Linux: systemd user service for background watcher

Migrating from older versions

claude-mirror reads older configs and manifests transparently — there is nothing you need to do by hand.

  • Configs without a backend field (any project YAML created before multi-backend support) are loaded as backend: googledrive. The field is filled in automatically the next time the config is written.
  • Manifests with legacy drive_file_id keys are still understood. Manifest.load accepts both the legacy drive_file_id and the current remote_file_id, so existing projects keep their full sync state across the upgrade.
  • Pre-v0.5.1 on-disk paths (claude_sync everywhere) — run claude-mirror migrate-state --apply once to rename local files and rewrite token paths. WebDAV/OneDrive listing predicates accept both old and new prefixes during the transition.

Disclaimer — use at your own risk

claude-mirror is provided as is, without warranty of any kind, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. By downloading, installing, or running this software you accept full and exclusive responsibility for any consequences of its use, including without limitation:

  • Data loss, corruption, or accidental deletion of your local files, your remote storage (Google Drive, Dropbox, OneDrive, WebDAV server, SFTP server, FTP/FTPS server, S3 bucket, SMB/CIFS share), or any backups thereof — whether caused by a bug, a misconfiguration, an interrupted sync, a network failure, a backend API change, an authentication problem, or otherwise.
  • Unintended overwrites during conflict resolution, pull, push, or restore operations.
  • Disclosure of file contents to anyone who has access to the configured remote folder, the Pub/Sub topic, the Slack channel, or the local machine. claude-mirror syncs whatever matches the configured file_patterns — review your patterns and exclude_patterns carefully before pushing.
  • Charges or quota consumption on Google Cloud, Microsoft Azure, Dropbox, or any other third-party service used as a backend.
  • Compliance with the terms of service of every backend, notification channel, and third-party API you point claude-mirror at.

You — the operator — are solely responsible for evaluating the suitability of claude-mirror for your use case, for keeping independent backups of any data you care about, and for testing the tool against non-critical data before relying on it in production. The authors and contributors are not liable for any direct, indirect, incidental, special, exemplary, or consequential damages arising from the use of this software, even if advised of the possibility of such damage.

If you do not accept these terms, do not download or run claude-mirror.


License

claude-mirror is free software released under the GNU General Public License, version 3 or later (GPL-3.0-or-later). The full text is in LICENSE.

In short:

  • You may use, modify, and redistribute this software, including in commercial settings.
  • If you distribute a modified version (or any work that incorporates claude-mirror's source), you must release your changes under the same GPL-3.0-or-later license and make the corresponding source available to recipients.
  • claude-mirror comes with NO WARRANTY, to the extent permitted by applicable law (see the Disclaimer above and Sections 15–17 of the GPL).

For the formal terms, see LICENSE or gnu.org/licenses/gpl-3.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

claude_mirror-0.5.70.tar.gz (994.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

claude_mirror-0.5.70-py3-none-any.whl (485.3 kB view details)

Uploaded Python 3

File details

Details for the file claude_mirror-0.5.70.tar.gz.

File metadata

  • Download URL: claude_mirror-0.5.70.tar.gz
  • Upload date:
  • Size: 994.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for claude_mirror-0.5.70.tar.gz
Algorithm Hash digest
SHA256 6c1ce2dead8a0f890a4f7c6dc018b0f25a48d01cf0749ec859f7d1c42d286c8d
MD5 3f22cff423269aacd8e8444adf8e9e8d
BLAKE2b-256 91dc1b8ea5fe0faee3e491cf08159909e85f974b5dd35748b1cb8fa7c5c482b1

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_mirror-0.5.70.tar.gz:

Publisher: publish.yml on alessiobravi/claude-mirror

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file claude_mirror-0.5.70-py3-none-any.whl.

File metadata

  • Download URL: claude_mirror-0.5.70-py3-none-any.whl
  • Upload date:
  • Size: 485.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for claude_mirror-0.5.70-py3-none-any.whl
Algorithm Hash digest
SHA256 9a2ebd26af7c30e1ddd1e9e989961c21466e6d78bf8648675d6e5d803ee3cbc8
MD5 63c18dd9f0e141400650350c4c6b9163
BLAKE2b-256 776a76e8859f08c5870df802bf04c1c6fe8141d948d86745848d7cf31602f19e

See more details on using hashes here.

Provenance

The following attestation bundles were made for claude_mirror-0.5.70-py3-none-any.whl:

Publisher: publish.yml on alessiobravi/claude-mirror

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page