Metadata-Version: 2.4
Name: cync-cli
Version: 0.7.0
Summary: Sync Claude Code conversations across machines — GitHub-authed CLI + server (Postgres metadata, R2 blobs).
Project-URL: Homepage, https://github.com/03hgryan/cync
Project-URL: Repository, https://github.com/03hgryan/cync
Author: 03hgryan
License: MIT License
        
        Copyright (c) 2026 03hgryan
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE 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. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
License-File: LICENSE
Keywords: claude,claude-code,cli,conversations,sync
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Utilities
Requires-Python: >=3.11
Requires-Dist: httpx<1,>=0.27
Requires-Dist: python-dotenv<2,>=1.0
Requires-Dist: rich<15,>=13
Requires-Dist: typer<1,>=0.12
Provides-Extra: dev
Requires-Dist: boto3<2,>=1.34; extra == 'dev'
Requires-Dist: fastapi<1,>=0.110; extra == 'dev'
Requires-Dist: mcp>=1.0; extra == 'dev'
Requires-Dist: pydantic<3,>=2; extra == 'dev'
Requires-Dist: pytest<9,>=8; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Requires-Dist: uvicorn[standard]<1,>=0.29; extra == 'dev'
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Provides-Extra: server
Requires-Dist: boto3<2,>=1.34; extra == 'server'
Requires-Dist: fastapi<1,>=0.110; extra == 'server'
Requires-Dist: pydantic<3,>=2; extra == 'server'
Requires-Dist: uvicorn[standard]<1,>=0.29; extra == 'server'
Description-Content-Type: text/markdown

# cync — code sync for Claude Code conversations

Sync your Claude Code conversations across machines, signed in with **GitHub**.
Your conversation **metadata** lives in your **Supabase** Postgres (per-user, RLS),
the **transcripts** live in your **Cloudflare R2** bucket, and a small FastAPI
server brokers both. Like git, but for your chats: `cync push` on one machine,
`cync pull` on another, and `claude --resume` picks up where you left off — plus
a web viewer to browse them.

```
Browser / CLI ── GitHub login (Supabase Auth) ──┐
                                                 ▼ JWT
                                            FastAPI  ── Postgres (metadata, per-user RLS)
                                                     ── R2 (transcript blobs)
```

## How it works
- Sign in once per machine with `cync login` (GitHub via Supabase, browser PKCE).
- A **project** is a first-class record you own; create one with `cync init <name>`
  and link a directory to it. Conversations are keyed by their Claude session UUID.
- `push` uploads `~/.claude/projects/<repo>/<id>.jsonl`; `pull` writes it back on the
  other machine and rewrites the embedded `cwd` so `claude --resume` just works.
- Everything is scoped to your GitHub user — projects/conversations are private to you.

## Install & use (hosted)

The command is `cync`. No server setup — it points at the hosted cync by default.

```bash
curl -fsSL https://cync.run/install.sh | sh
```

Installs a self-contained binary to `~/.local/bin` (no Python needed, MCP bundled).
macOS Apple Silicon and Linux x86_64. For other platforms: `pipx install cync-cli`.

Then:
```bash
cync login                   # sign in with GitHub
cd ~/path/to/your/project
cync init myproject && cync push     # machine A
cync link myproject && cync pull     # machine B → claude --resume
```

## Self-hosting

Prefer your own stack? Point the CLI at your deployment via `CYNC_SERVER_URL`
(env or `~/.config/cync/config.toml`), then deploy the server + provision your
own Supabase + R2:

### 1. Provision (one time)
- **Cloudflare R2:** create a bucket (default name `cync`) and an API token with
  **Object Read & Write**.
- **Supabase:** create a project, then run [`supabase/schema.sql`](supabase/schema.sql)
  in the SQL editor. Enable **Auth → Providers → GitHub** (create a GitHub OAuth App
  with callback `https://<ref>.supabase.co/auth/v1/callback`). Under **Auth → URL
  Configuration**, add redirect URLs `http://127.0.0.1:8765/callback` (CLI) and
  `http://localhost:5173/auth/callback` (web).

### 2. Server
```bash
uv venv && source .venv/bin/activate
uv pip install -e ".[server]"
cp .env.example .env     # fill in R2_* and SUPABASE_* (URL, anon, service_role)
cync-server              # http://127.0.0.1:8787
```
> For cross-machine use, expose the server over **HTTPS** (Tailscale, Fly.io, Railway).
> The CLI refuses non-HTTPS server URLs except on localhost.

### 3. CLI (each machine)
```bash
uv pip install -e .                      # base install
export CYNC_SERVER_URL=http://127.0.0.1:8787   # or your https URL / config.toml
cync login                               # sign in with GitHub (opens a browser)

cd ~/path/to/your/project
cync init droneforge                     # create + link a project (machine A)
cync push
# on machine B:  cync link droneforge && cync pull  →  claude --resume
```

### 4. Web viewer (optional)
```bash
cd client
pnpm install
cp .env.example .env   # PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY, CYNC_SERVER_URL
pnpm dev               # http://localhost:5173 → "Sign in with GitHub"
```
The SvelteKit server holds the token (Supabase SSR cookies); browse at `/p/<project>`.

## Commands
| Command | What it does |
|---|---|
| `cync login` / `logout` / `whoami` | GitHub auth session |
| `cync init <name>` | create a project + link this directory |
| `cync link <name>` | link this directory to an existing project |
| `cync unlink` | remove this directory's link |
| `cync status` | show login, linked project, and local-vs-server sync state |
| `cync push [id]` | upload this project's conversation(s) |
| `cync pull [id]` | download into `~/.claude` (overwrite/add) |
| `cync list` | list this project's conversations |
| `cync rm <id>` | delete one conversation from the server (local copy kept) |
| `cync open` | open the web viewer for this project |
| `cync install-hooks` | auto-sync via Claude Code hooks (pull on start, push on end) |
| `cync project list` / `rm <name>` | manage your projects |
| `cync --version` | print the version |

## MCP server (optional)
Let Claude (or any MCP host) read your synced conversations *as context* — pull a
past chat into the model mid-conversation, not just sync files.

```bash
pipx install 'cync-cli[mcp]'   # the mcp extra
cync login                     # the server uses your cync session
```
Add it to your MCP host (Claude Code `.mcp.json`, Claude Desktop config, etc.):
```json
{ "mcpServers": { "cync": { "command": "cync", "args": ["mcp"] } } }
```
Tools: `list_projects`, `list_conversations(project)`, `get_conversation(project, id)`.

## Migrating from v0.2
If you have v0.2 (static-token) data still in R2, after `cync login`:
```bash
python scripts/migrate_legacy.py --slug <name> --email <your-github-email> [--purge]
```
Or simply re-`cync push` from a machine that has the conversations locally.

## Security
- Per-user GitHub auth (Supabase JWT); every project/conversation is owner-scoped,
  with Postgres RLS as a backstop. The server holds R2 + Supabase keys; clients never do.
- CLI login uses PKCE + a single-use loopback callback; the session (refresh token)
  is stored `0600` at `~/.config/cync/session.json`.
- Request bodies are capped (`CYNC_MAX_BODY_BYTES`, default 64 MB).

## Layout
```
src/cync/        server (FastAPI), CLI, auth, Supabase + R2 stores
client/          SvelteKit web viewer (GitHub login)
supabase/        Postgres schema + RLS
scripts/         legacy migration
```

## Limitations
- Last-writer-wins; no version history yet.
- Syncs the `.jsonl` transcript only (enough for `--resume`); not `todos/` sidecars.
- Don't `push` a conversation that's open in Claude Code — quit first so it isn't mid-write.
- Listing reads one object per conversation from Postgres (fine for personal scale).
