Skip to main content

Sync Claude Code knowledge across machines via a shared git repository and AI-powered synthesis

Project description

claude-sync

Synchronize Claude Code knowledge (CLAUDE.md files, todos) across multiple development machines using a shared git repository and an AI-powered synthesis pipeline.

How it works

Each machine runs the claude-sync CLI to push snapshots of its .claude/ artifacts into a shared git repo. A CI pipeline then calls the Anthropic API to intelligently merge the knowledge from all machines into a single consolidated version. Machines pull and apply the consolidated result locally.

Machine A ──push──> sync repo ──CI pipeline──> consolidated/ ──pull──> Machine A
Machine B ──push──>            (Claude API merge)              ──pull──> Machine B

The system has two components:

  • Client CLI (claude-sync) — installed on each dev machine, handles push/pull/apply
  • Pipeline (pipeline/synthesize.py) — lives inside the sync repo, runs on CI (or locally)

The Anthropic API key lives in your CI environment (or local shell for the local provider). It never reaches other client machines.


Repository layout (sync repo)

claude-knowledge-sync/
├── .gitlab-ci.yml             # or .github/workflows/synthesize.yml for GitHub
├── machines/                  # raw snapshots, one subdir per machine
│   ├── desktop/
│   │   ├── global/
│   │   │   ├── CLAUDE.md
│   │   │   └── settings.json
│   │   └── projects/
│   │       └── my-app/
│   │           ├── CLAUDE.md
│   │           └── memory/
│   │               └── MEMORY.md
│   └── laptop/
│       └── global/
│           └── CLAUDE.md
├── consolidated/              # merged synthesis output
│   ├── global/
│   │   ├── CLAUDE.md
│   │   └── settings.json
│   └── projects/
│       └── my-app/
│           ├── CLAUDE.md
│           └── memory/
│               └── MEMORY.md
├── meta/
│   ├── last-synthesis.json    # hashes of inputs at last synthesis
│   └── sync-log.json          # append-only log of synthesis runs
└── pipeline/
    ├── synthesize.py
    ├── models.py
    ├── requirements.txt
    └── prompts/
        ├── synthesize_claude_md.txt
        └── synthesize_critic.txt

Prerequisites

  • Python 3.11+
  • Git
  • A git-hosting account (GitLab, GitHub, or any platform) for the sync repo

Choosing a CI provider

claude-sync supports three providers for running the synthesis pipeline:

Provider How synthesis runs Anthropic API key location
gitlab GitLab CI pipeline (self-hosted or gitlab.com) GitLab CI/CD variable
github GitHub Actions workflow GitHub repository secret
local Subprocess on your machine Your local environment

Use --provider during claude-sync init to select one. The default is gitlab.


Part 1: Sync repo setup (one-time, manual)

Create an empty git repository on your chosen platform (e.g. claude-knowledge-sync), then clone it locally.

GitLab setup

  1. Create the project and note the project ID (shown at the top of Settings → General).

  2. Protect the main branch — Settings → Repository → Protected Branches. Required for protected CI/CD variables.

  3. Add the Anthropic API key — Settings → CI/CD → Variables → Add variable:

    Field Value
    Key ANTHROPIC_API_KEY
    Value your Anthropic API key
    Masked yes
    Protected yes
    Expand variable reference no
  4. Create a Pipeline Trigger Token — Settings → CI/CD → Pipeline trigger tokens → Add new token. Copy the glptt-... value.

  5. Create a Project Access Token for status polling — Settings → Access Tokens → Add new token with role Maintainer and scope read_api. Copy the glpat-... value.

  6. (Optional) Create a scheduled pipeline — Build → Pipeline schedules → New schedule. Suggested cron: 0 3 * * * (daily at 3 AM UTC), branch main.

  7. Ensure a runner is available — the pipeline uses python:3.12-slim. Verify a runner with the Docker executor is attached (Settings → CI/CD → Runners).

  8. Allow CI push-back — Settings → CI/CD → Token Access → enable "Allow Git push requests to the repository". Without this the CI job fails with a 403 when it tries to push synthesized results.

GitHub setup

  1. Create the repository (public or private).

  2. Add the Anthropic API key — Settings → Secrets and variables → Actions → New repository secret: ANTHROPIC_API_KEY.

  3. Create a Personal Access Token with scopes repo and workflow (Settings → Developer settings → Personal access tokens → Fine-grained tokens or classic tokens). This is what you enter as --github-token during init.

Local setup

No remote CI is needed. You must have ANTHROPIC_API_KEY set in your shell environment before running claude-sync trigger or claude-sync sync.


Part 2: Client machine setup

Repeat these steps on every machine you want to sync.

1. Clone the sync repo locally

git clone https://github.com/yourname/claude-knowledge-sync.git ~/claude-knowledge-sync
# or for GitLab:
git clone https://gitlab.example.com/yourname/claude-knowledge-sync.git ~/claude-knowledge-sync

2. Install the claude-sync CLI

Use pipx, which installs CLI tools in isolated environments and automatically adds them to your PATH:

pip install pipx
py -m pipx ensurepath   # one-time: adds pipx's bin dir to PATH

Windows note: pip install pipx may put pipx.exe in a Scripts directory that is not yet on PATH. If pipx ensurepath is not recognized, use py -m pipx ensurepath instead — this runs pipx as a Python module and bypasses the PATH issue.

Restart your terminal, then install from PyPI:

pipx install clsync

Alternatively, install from a local clone of this repository:

pipx install .

Verify:

claude-sync --help

For development (editable install with test dependencies — use pip, not pipx):

pip install -e ".[dev]"

3. Initialize claude-sync

Run claude-sync init and follow the prompts, or pass flags directly.

GitLab:

claude-sync init \
  --provider gitlab \
  --repo-path ~/claude-knowledge-sync \
  --gitlab-url https://gitlab.example.com \
  --project-id 42 \
  --trigger-token glptt-... \
  --access-token glpat-...

GitHub:

claude-sync init \
  --provider github \
  --repo-path ~/claude-knowledge-sync \
  --github-token ghp_... \
  --github-repo yourname/claude-knowledge-sync

Local:

claude-sync init \
  --provider local \
  --repo-path ~/claude-knowledge-sync

init prompts interactively for any values not supplied as flags. The machine ID defaults to your hostname. If --repo-path does not yet contain a git repo, you will be offered the option to clone from a remote URL.

After init, claude-sync scaffolds the appropriate CI config into the sync repo (.gitlab-ci.yml for GitLab, .github/workflows/synthesize.yml for GitHub) and offers to commit and push it.

Credentials are stored in ~/.claude-sync/config.toml, outside any repository. On macOS/Linux the file permissions are set to 600 automatically.

4. Register projects

Register each local project directory you want to sync:

claude-sync add-project ~/dev/my-app
claude-sync add-project ~/dev/another-project

Or scan a directory tree to discover projects automatically:

claude-sync discover --root ~/dev

This walks ~/dev up to 2 levels deep, finds directories containing a .claude/ directory or existing Claude memory context, and offers to add them.

Manage registered projects

List all registered projects and their sync status:

claude-sync list-projects

Remove a project from syncing:

claude-sync remove-project ~/dev/my-app

# Also delete local Claude memory files (~/.claude/projects/{slug}/)
claude-sync remove-project ~/dev/my-app --purge

Move a registered project to a new path (updates config and Claude internal files):

claude-sync move-project ~/dev/old-path ~/dev/new-path

# Also rename the directory on disk
claude-sync move-project ~/dev/old-path ~/dev/new-path --rename-dir

Configuration reference

Config file location: ~/.claude-sync/config.toml

GitLab provider:

[client]
machine_id = "desktop"

[provider]
type = "gitlab"

[gitlab]
url = "https://gitlab.example.com"
project_id = 42
trigger_token = "glptt-..."   # pipeline trigger token
access_token  = "glpat-..."   # personal access token (read_api scope)

[repo]
local_path = "/home/user/claude-knowledge-sync"

[projects]
paths = [
    "/home/user/dev/my-app",
    "/home/user/dev/another-project",
]
auto_discover = false          # if true, scan discover_roots on every push
discover_roots = []            # directories to scan when auto_discover = true
discover_max_depth = 2         # how many directory levels deep to scan

[safety]
backup_before_apply = true     # back up local CLAUDE.md before overwriting

GitHub provider — replace [provider] and [gitlab] with:

[provider]
type = "github"

[github]
token = "ghp_..."              # PAT with repo + workflow scopes
repo = "yourname/claude-knowledge-sync"
workflow_id = "synthesize.yml" # filename in .github/workflows/

Local provider — replace [provider] with, and omit [gitlab]/[github]:

[provider]
type = "local"

Credentials are stored directly in the config file. Ensure its permissions are restricted (chmod 600 ~/.claude-sync/config.toml on macOS/Linux — done automatically by init).


Daily usage

Push your current state and trigger synthesis

claude-sync push

This:

  1. Pulls the latest remote state (rebase)
  2. Copies your local .claude/CLAUDE.md files into machines/{machine_id}/ in the sync repo
  3. Commits and pushes if anything changed
  4. Triggers the CI synthesis pipeline

To push without triggering the pipeline:

claude-sync push --no-trigger

Pull and apply the latest consolidated result

claude-sync pull

This:

  1. Pulls the latest remote state (rebase)
  2. Checks whether Claude Code is currently running (warns and aborts if so)
  3. For each scope with large changes (>50% of lines differ), asks for confirmation
  4. Applies consolidated CLAUDE.md files to your local .claude/ directories
  5. Prints warnings for any <!-- CONFLICT: --> or <!-- UNVERIFIED: --> markers found in applied files

To apply even if Claude Code is running (still confirms large diffs):

claude-sync pull --allow-running

To skip all safety checks (running process detection and large-diff confirmation):

claude-sync pull --force

Push, synthesize, and pull in one command

claude-sync sync

This runs push → waits for the pipeline to complete → pull, in sequence.

Trigger the pipeline without pushing

claude-sync trigger          # fire and forget
claude-sync trigger --wait   # wait for completion and print result

Bootstrap CLAUDE.md files from existing context

If you already have Claude Code memory and session history on a machine but no CLAUDE.md files yet, bootstrap can generate them automatically using the Anthropic API:

# Bootstrap all registered projects and global ~/.claude/CLAUDE.md
claude-sync bootstrap --api-key sk-ant-...

# Or set the environment variable and omit the flag
export ANTHROPIC_API_KEY=sk-ant-...
claude-sync bootstrap

# Bootstrap only the global CLAUDE.md
claude-sync bootstrap --global-only

# Bootstrap only a specific registered project
claude-sync bootstrap --project ~/dev/my-app

# Overwrite existing files without confirmation
claude-sync bootstrap --force

When an existing CLAUDE.md would be overwritten, bootstrap shows a line-count diff and asks for confirmation unless --force is given.

Note: bootstrap calls the Anthropic API directly from the client machine using your API key. Unlike the sync pipeline, this is a one-time local operation — it is not required for normal push/pull sync to work.

Check synthesis status

claude-sync status

Prints the last synthesis timestamp and a table of scopes with machine IDs and file hashes.


Command reference

Command Description
claude-sync init Initialize config for this machine
claude-sync add-project PATH Register a project directory
claude-sync remove-project PATH Unregister a project (optionally --purge local Claude data)
claude-sync move-project OLD NEW Move a registered project to a new path
claude-sync list-projects List registered projects and their sync status
claude-sync discover --root PATH Find and add projects under a directory
claude-sync bootstrap Generate CLAUDE.md files from existing Claude memory and session history
claude-sync push Snapshot local files, push, trigger pipeline
claude-sync pull Pull consolidated files and apply locally
claude-sync sync Full push → synthesize → pull cycle
claude-sync trigger Trigger the CI pipeline
claude-sync status Show last synthesis metadata

Run claude-sync COMMAND --help for full option details on any command.


How synthesis works

When the CI pipeline runs, pipeline/synthesize.py:

  1. Scans machines/ to find all scopes (global, projects/my-app, etc.)
  2. Compares SHA-256 hashes of current input files against the previous run (stored in meta/last-synthesis.json)
  3. For each scope that changed, calls the Anthropic API (Claude) with a merge prompt containing all machine versions
  4. Runs a second critic pass — another Claude call that checks the merged output for claims not traceable to any source file (hallucination detection)
  5. If the critic finds more than 3 unverified claims, the scope is rejected and the previous consolidated file is left untouched
  6. If 1–3 unverified claims are found, they are embedded as <!-- UNVERIFIED: ... --> HTML comments in the output
  7. Writes the merged result to consolidated/{scope}/CLAUDE.md
  8. Updates meta/last-synthesis.json and appends to meta/sync-log.json

If only one machine has content for a scope, it is copied directly without an API call.

Conflict markers

When two machines contradict each other and the synthesizer cannot determine which is correct, it writes:

<!-- CONFLICT: [description] — resolve manually -->

claude-sync pull scans applied files for these markers and prints them as warnings so you can resolve them manually.


Security notes

Secret Where it lives Never in
ANTHROPIC_API_KEY CI/CD variable (GitLab or GitHub) or local env Client config file, code, repo
GitLab trigger token (glptt-...) ~/.claude-sync/config.toml Code, repo, CI
GitLab access token (glpat-...) ~/.claude-sync/config.toml Code, repo, CI
GitHub token (ghp_...) ~/.claude-sync/config.toml Code, repo, CI

GitLab: The pipeline push-back uses CI_JOB_TOKEN, injected automatically by GitLab. No extra token is needed. GitLab does not trigger a new pipeline when CI_JOB_TOKEN is used for a push.

GitHub: Synthesis commits are pushed using the GITHUB_TOKEN provided automatically by Actions. The workflow has contents: write permission.

The config file lives at ~/.claude-sync/config.toml, outside any repository, and is never committed. Its permissions are restricted to 600 automatically on Unix. Do not check it into version control.

Do not store credentials, passwords, or API keys inside CLAUDE.md files — they are committed to the sync repo and visible to anyone with repository access.


Running tests

pip install -e ".[dev]"
pytest tests/ -q

All tests use temporary directories and mocked API calls. No real GitLab or Anthropic credentials are needed to run the test suite.


Development

The client CLI source is under src/claude_sync/:

File Purpose
cli.py Typer CLI entry point — all commands
config.py Config loading and saving
snapshot.py Copy local .claude/ files into the sync repo
apply.py Copy consolidated files back to local .claude/
bootstrap.py Generate CLAUDE.md from existing Claude memory/session history
git_ops.py Git wrapper (pull, commit, push)
gitlab_api.py Low-level GitLab REST API calls
ci_provider.py CI provider abstraction (GitLab, GitHub Actions, local)
lock.py Detect whether Claude Code is running
scaffold.py Scaffold CI config and pipeline scripts into a new sync repo
models.py Pydantic data models
claude_paths.py Resolve Claude Code paths (memory dir, project slug)

The pipeline source is under pipeline/:

File Purpose
synthesize.py Main CI entry point
models.py Pydantic models (subset of client models)
requirements.txt anthropic, pydantic
prompts/synthesize_claude_md.txt Merge prompt template
prompts/synthesize_critic.txt Hallucination-check prompt template

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

clsync-1.0.3.tar.gz (10.4 MB view details)

Uploaded Source

Built Distribution

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

clsync-1.0.3-py3-none-any.whl (41.8 kB view details)

Uploaded Python 3

File details

Details for the file clsync-1.0.3.tar.gz.

File metadata

  • Download URL: clsync-1.0.3.tar.gz
  • Upload date:
  • Size: 10.4 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for clsync-1.0.3.tar.gz
Algorithm Hash digest
SHA256 ac8f56a83a9890bc0be8086fd8f3cdc77855e223f81b575e9a29f4e9826656f6
MD5 ada4276beb4b5408fd811f55c64ec802
BLAKE2b-256 b76e55f6f71aaaedf670e1141c2399bc8794b6e6d6fdab1ff84b8855767c3439

See more details on using hashes here.

File details

Details for the file clsync-1.0.3-py3-none-any.whl.

File metadata

  • Download URL: clsync-1.0.3-py3-none-any.whl
  • Upload date:
  • Size: 41.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.13

File hashes

Hashes for clsync-1.0.3-py3-none-any.whl
Algorithm Hash digest
SHA256 5c74f5d5c7d70c21a7cfe851b46e975316c55cfeb847741fa2d2b28774a5cec5
MD5 bde04feddcbb7290aa2c6060dfd787cd
BLAKE2b-256 bfcc86fb2cb4e81ddc694fb65cb49fd84826e247d25498b215bf5f7b242b6d0e

See more details on using hashes here.

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