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
-
Create the project and note the project ID (shown at the top of Settings → General).
-
Protect the main branch — Settings → Repository → Protected Branches. Required for protected CI/CD variables.
-
Add the Anthropic API key — Settings → CI/CD → Variables → Add variable:
Field Value Key ANTHROPIC_API_KEYValue your Anthropic API key Masked yes Protected yes Expand variable reference no -
Create a Pipeline Trigger Token — Settings → CI/CD → Pipeline trigger tokens → Add new token. Copy the
glptt-...value. -
Create a Project Access Token for status polling — Settings → Access Tokens → Add new token with role Maintainer and scope
read_api. Copy theglpat-...value. -
(Optional) Create a scheduled pipeline — Build → Pipeline schedules → New schedule. Suggested cron:
0 3 * * *(daily at 3 AM UTC), branchmain. -
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). -
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
-
Create the repository (public or private).
-
Add the Anthropic API key — Settings → Secrets and variables → Actions → New repository secret:
ANTHROPIC_API_KEY. -
Create a Personal Access Token with scopes
repoandworkflow(Settings → Developer settings → Personal access tokens → Fine-grained tokens or classic tokens). This is what you enter as--github-tokenduringinit.
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 pipxmay putpipx.exein a Scripts directory that is not yet on PATH. Ifpipx ensurepathis not recognized, usepy -m pipx ensurepathinstead — 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:
- Pulls the latest remote state (rebase)
- Copies your local
.claude/CLAUDE.mdfiles intomachines/{machine_id}/in the sync repo - Commits and pushes if anything changed
- 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:
- Pulls the latest remote state (rebase)
- Checks whether Claude Code is currently running (warns and aborts if so)
- For each scope with large changes (>50% of lines differ), asks for confirmation
- Applies consolidated
CLAUDE.mdfiles to your local.claude/directories - 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:
bootstrapcalls 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:
- Scans
machines/to find all scopes (global,projects/my-app, etc.) - Compares SHA-256 hashes of current input files against the previous run (stored in
meta/last-synthesis.json) - For each scope that changed, calls the Anthropic API (Claude) with a merge prompt containing all machine versions
- Runs a second critic pass — another Claude call that checks the merged output for claims not traceable to any source file (hallucination detection)
- If the critic finds more than 3 unverified claims, the scope is rejected and the previous consolidated file is left untouched
- If 1–3 unverified claims are found, they are embedded as
<!-- UNVERIFIED: ... -->HTML comments in the output - Writes the merged result to
consolidated/{scope}/CLAUDE.md - Updates
meta/last-synthesis.jsonand appends tometa/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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file clsync-1.0.4.tar.gz.
File metadata
- Download URL: clsync-1.0.4.tar.gz
- Upload date:
- Size: 485.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7ca703404bb94694142a5aa5bde7e5e533379723943c548874c133d61209c5f7
|
|
| MD5 |
ca62f38007001bfb814d3040472059e5
|
|
| BLAKE2b-256 |
4aa29d015911d79f93fb53692fe8b5cdb2f7ed267fa522e50e6013cc21a14537
|
File details
Details for the file clsync-1.0.4-py3-none-any.whl.
File metadata
- Download URL: clsync-1.0.4-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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
713a1d69e8b37e510e2a20182d33cb117b807e57a426dd13f93ba97f6cac00a3
|
|
| MD5 |
00a9c5e62323dd802e5b50d3c12d878f
|
|
| BLAKE2b-256 |
4a04e3531405576fa9fda85e84ab516932d94257b7bfbaf823b34621a6ec3dab
|