@slvnlrt/opencode-bridge
v0.2.6
Published
Sync OpenCode config/sessions to GitHub + bidirectional bridge with Claude Code
Readme
opencode-bridge
Sync OpenCode config and sessions across machines via GitHub, with bidirectional Claude Code session conversion.
npm install -g @slvnlrt/opencode-bridgeWhat it does
opencode-bridge syncs your OpenCode config, sessions, and Claude Code JSONL files through a private GitHub repo. One command does everything:
opencode-bridge syncThis runs the full pipeline: backup -> pull -> resolve conflicts -> export -> import -> push. Config, sessions, and Claude Code JSONL files all travel through a single private GitHub repo. Every push is a squash orphan commit -- the repo never grows beyond one commit, which prevents Git LFS quota exhaustion.
Running opencode-bridge with no arguments defaults to sync.
Quick Start
1. Install
One command:
npm install -g github:slvnlrt/opencode-bridgeOr from source:
git clone https://github.com/slvnlrt/opencode-bridge.git
cd opencode-bridge
npm install && npm run build && npm link2. Set your remote
# macOS/Linux -- add to ~/.zshrc or ~/.bashrc
export SYNC_REMOTE_URL="https://github.com/YOUR_USER/YOUR_PRIVATE_REPO.git"
# Windows -- run once in PowerShell
# [System.Environment]::SetEnvironmentVariable("SYNC_REMOTE_URL", "https://github.com/YOUR_USER/YOUR_PRIVATE_REPO.git", "User")3. Sync
opencode-bridge sync # Full sync (default)
opencode-bridge sync --dry-run # Preview without changesCLI Reference
Primary Command: sync
Full sync: backup -> pull -> resolve conflicts -> export -> import -> push.
| Flag | Description |
|---|---|
| --import-only | Only import (backup -> pull -> import) |
| --export-only | Only export (backup -> pull -> export -> push) |
| --no-push | Skip the final push to GitHub |
| --no-pull | Skip pulling from remote |
| --dry-run | Show what would happen without making changes |
| -f, --force | Skip conflict prompts, use newer-wins |
| --auto-rollback | Auto-restore database backup on failure |
Running with no arguments defaults to sync.
Bridge Commands
| Command | Description |
|---|---|
| import-from-claude | Import Claude Code sessions into OpenCode |
| export-to-claude | Export OpenCode sessions to Claude Code |
| status | Show sessions in each tool and their sync state |
| watch | Watch for changes and sync automatically |
Options for import-from-claude / export-to-claude:
| Flag | Description |
|---|---|
| -p, --project <path> | Source project path (default: all) |
| -s, --session <id> | Specific session ID |
| --since <date> | Only sync sessions modified after this date |
| --dry-run | Show what would be synced without writing |
Options for watch:
| Flag | Description |
|---|---|
| -d, --direction <dir> | Sync direction: both, to-claude, from-claude (default: both) |
GitHub Aliases (legacy)
These are convenience aliases that route through sync:
| Command | Description |
|---|---|
| push | Push config/sessions to GitHub (alias for sync --export-only) |
| pull | Pull config/sessions from GitHub (alias for sync --import-only --no-push) |
| github-status | Show GitHub sync status for config and sessions |
Options for push / pull:
| Flag | Description |
|---|---|
| -t, --target <target> | What to sync: config, sessions, both (default: both) |
| -f, --force | Force push/pull (override conflicts or discard local changes) |
How It Works
Sync Mapping
Local directories map to paths inside the sync repo:
| Local Path | Repo Path |
|---|---|
| ~/.config/opencode/ | repo root |
| ~/.local/share/opencode/ | _data/ |
| ~/.local/state/opencode/ | _state/ |
| ~/.agents/skills/ | _agents/ |
| Claude Code JSONL sessions | _claude/ |
Squash Push
Every push creates a single orphan commit -- the repo always contains exactly one commit. This means:
- Git LFS only ever stores one copy of the SQLite database
- No history growth, no accumulating LFS objects
- Prevents the LFS quota exhaustion that occurs with normal incremental commits
JSONL Backups
Before each push, Claude Code session files (_claude/) are archived to _backups/ as a .tar.gz file. The 5 most recent archives are retained; older ones are automatically pruned.
SQLite Safety
- Database backup via the SQLite
.backupcommand (safe with concurrent readers/writers in WAL mode) - Pull merges remote sessions into the local DB instead of replacing it -- no data loss
- Integrity verification after pull
- Migration record repair when pulling an older database version
- Sync runs while OpenCode is open -- no need to close it
Git LFS
The SQLite database is tracked via Git LFS. After each push, git lfs prune runs to clean the local LFS cache.
Prerequisites
- Node.js v20+
- Git + Git LFS
- SQLite3 CLI -- for database backup and integrity checks
- A private GitHub repository -- create one, keep it empty
Environment Variables
| Variable | Required | Description |
|---|---|---|
| SYNC_REMOTE_URL | Yes | Your private GitHub repo URL |
| SYNC_CONFIG_ROOT | No | Override config dir (default: ~/.config/opencode) |
| SYNC_DATA_ROOT | No | Override data dir (default: ~/.local/share/opencode) |
| SYNC_STATE_ROOT | No | Override state dir (default: ~/.local/state/opencode) |
| SYNC_AGENTS_ROOT | No | Override agents dir (default: ~/.agents/skills) |
| SYNC_SKIP_LFS | No | Set to 1 to skip Git LFS |
Troubleshooting
LFS quota exceeded Delete and recreate the remote repository. The squash push strategy prevents future quota growth since only one commit (and one LFS object) ever exists.
"OpenCode processes still running" This message no longer appears -- sync now works while OpenCode is running. If you see it in an older version, upgrade to the latest build.
Stash pop failed Resolve manually:
cd ~/.config/opencode && git stash popDatabase integrity check failed Restore from a recent backup:
cp ~/.local/share/opencode-bridge/backups/<latest>.db ~/.local/share/opencode/opencode.dbPush failed (force-with-lease rejected) Another machine pushed since your last pull. Pull first:
opencode-bridge pull --force
opencode-bridge syncNew to This?
If you are not familiar with Git, GitHub CLI, or environment variables, paste this prompt into OpenCode and let it handle the setup:
I want to sync my OpenCode config and sessions across multiple machines.
Help me set up everything from scratch:
1. Check if Git, Git LFS, Node.js, and SQLite3 are installed -- install anything missing
2. Check if gh (GitHub CLI) is installed -- if not, install it, then help me log in to GitHub
3. Create a private GitHub repo for storing my sync data
4. Clone the opencode-bridge project, run npm install && npm run build && npm link
5. Set the SYNC_REMOTE_URL environment variable for me
6. Run "opencode-bridge sync --dry-run" to verify everything worksProject Structure
opencode-bridge/
├── src/
│ ├── cli.ts # CLI entry point (commander)
│ ├── config.ts # Shared configuration
│ │
│ ├── commands/ # CLI command handlers
│ │ ├── sync.ts # sync (primary command)
│ │ ├── github-sync.ts # github-status
│ │ ├── import.ts # import-from-claude
│ │ ├── export.ts # export-to-claude
│ │ ├── status.ts # status
│ │ └── watch.ts # watch
│ │
│ ├── github/ # GitHub sync engine
│ │ ├── git.ts # Git CLI wrapper + helpers
│ │ ├── squash.ts # Orphan-commit squash push
│ │ ├── fs-utils.ts # File system utilities
│ │ ├── paths.ts # XDG path resolution
│ │ ├── process.ts # OpenCode process detection/kill
│ │ ├── sqlite-ops.ts # SQLite backup, verify, migration repair
│ │ ├── sync-config.ts # Config staging/pull logic
│ │ └── sync-sessions.ts # Session staging/pull logic
│ │
│ ├── adapters/ # Data adapters for bridge
│ │ ├── claude/ # Claude Code JSONL reader/writer
│ │ └── opencode/ # OpenCode SQLite reader/writer
│ │
│ ├── converter/ # Session format converters
│ │ ├── claude-to-opencode.ts # JSONL -> SQLite
│ │ ├── opencode-to-claude.ts # SQLite -> JSONL
│ │ └── id-map.ts # Deterministic ID mapping
│ │
│ └── sync/ # Sync state and orchestration
│ ├── engine.ts # Import/export orchestration
│ ├── state.ts # Sync state tracking
│ ├── conflict.ts # Conflict detection and resolution
│ ├── merge.ts # Session-level DB merge (latest action wins)
│ ├── backup.ts # Database + JSONL backup/archive
│ ├── jsonl-sync.ts # JSONL file staging/restore
│ └── prompt.ts # Interactive prompts
│
├── tests/
│ ├── converter.test.ts # Converter unit tests
│ └── github-utils.test.ts # GitHub sync utility tests
│
├── docs/ # SVG diagrams
├── package.json
├── tsconfig.json
├── tsup.config.ts
└── README.mdSync repo structure (what your private GitHub repo looks like):
your-sync-repo/
├── config.json, skills/, ... # OpenCode config (repo root)
├── _data/ # OpenCode data (auth, db)
├── _state/ # OpenCode state (frecency, kv)
├── _agents/ # Skills CLI installs
├── _claude/ # Claude Code JSONL sessions
└── _backups/ # Archived JSONL snapshots (5 retained)Development
npm install # Install dependencies
npm run build # Build to dist/
npm run dev # Build + watch
npm test # Run tests (vitest)
npm run typecheck # TypeScript type checkPublishing a new version
npm version patch # Bump version (0.2.2 → 0.2.3)
npm publish --access public
git push origin main --tagsUsers update with: npm install -g @slvnlrt/opencode-bridge
