opencode-sm
v0.3.8
Published
OpenCode Settings Manager — sync your OpenCode configs, agents, skills, sessions and more across machines via GitHub
Downloads
492
Readme
opencode-sm — OpenCode Settings Manager
opencode-sm is an OpenCode plugin that synchronizes your OpenCode configuration — configs, agents, skills, commands, tools, themes, plugins, and more — across multiple machines via a private GitHub repository.
Features
- Full configuration sync —
opencode.json,tui.json,AGENTS.md, agents, skills, commands, tools, themes, and plugins - Automatic secret sanitization — API keys, tokens, passwords, and private keys are redacted before upload (unless explicitly opted in)
- AES-256-GCM encryption — sensitive config values can be encrypted end-to-end with a local key; never stored in plaintext in the repo
- Extra config paths — sync custom files and directories beyond the OpenCode defaults via
extraConfigPaths .syncignoresupport — skip specific files or glob patterns via a.syncignorefile orexcludePathsconfig- Machine isolation — each machine syncs to its own
machines/<name>/subfolder, preventing cross-machine file pollution - Machine identification — every git commit includes the machine name
- Per-machine overrides —
opencode-sm.overrides.jsoncis never synced; settings here apply only to the local machine - Bidirectional sync — push local changes up, pull remote changes down, with conflict detection
- Mirror sync —
/sync-upis an exact mirror of local state, including deletions - Automatic backups — local state is backed up before any destructive operation; up to 10 write manifests are retained
- Atomic file writes — files are written via tmp+rename, eliminating partial-write corruption
- Safe mode by default — startup is non-destructive; logs divergence, never modifies files without an explicit command
- Preflight health checks — verifies
ghCLI,git, GitHub authentication, and git identity before any operation - Conflict detection — files modified both locally and remotely are skipped and logged; no silent overwrites
- Event-driven startup sync — sync triggers on
server.connected, guaranteeing the TUI is ready before notifications appear - Toast notifications — every command completion surfaces a descriptive popup in the TUI
- History and recovery —
/sync-logshows commit history;/sync-restorerestores from a local backup
Prerequisites
Node.js 18 or later
OpenCode installed and configured
Git installed and available on
PATHGitHub CLI (
gh) installed and authenticatedgh auth loginGit identity configured:
git config --global user.name "Your Name" git config --global user.email "[email protected]"
Installation
opencode plugin add opencode-smOr add to your opencode.json:
{
"plugin": ["opencode-sm"]
}Quick Start
First machine — create a sync repository
/sync-new my-configThis will:
- Create a private GitHub repository named
my-config - Clone it locally to
~/.local/share/opencode/opencode-sm/repo - Collect your current OpenCode configuration files
- Sanitize secrets (API keys, tokens, etc.)
- Push everything with a commit like
[my-laptop] Initial sync from opencode-sm
Second machine — connect to the existing repo
/sync-connect my-config
# or with explicit owner:
/sync-connect your-user/my-configRegular workflow
# After making changes on machine A:
/sync-up
# On machine B to receive those changes:
/sync-downCommands
| Command | Description | Use case |
|---------|-------------|----------|
| /sync-new [repo] | Create a new private GitHub repo and push current configs | First machine setup |
| /sync-connect [repo] | Connect this machine to an existing sync repo | Subsequent machines |
| /sync-up | Upload local config changes to GitHub | After modifying configs locally |
| /sync-down | Download remote config changes from GitHub | After configs changed elsewhere |
| /sync-check | Show diff between local configs and the remote repo | Preview before up/down |
| /sync-repo | Display current sync repo configuration | Diagnostics |
| /sync-fix | Propagate pending local deletions to the remote repo | After deleting files locally |
| /sync-log | Show recent commit history from the sync repo | Audit trail |
| /sync-restore [backup] | Restore local files from a previous backup | Recovery |
/sync-log
Displays the recent commit history from the GitHub sync repository — commit hashes, authors, dates, and messages. Use this to audit what was synced and when, or to identify a point before running /sync-restore.
/sync-restore [backup-name]
Restores local OpenCode configuration files from a backup created during a previous sync. Backups are created automatically by /sync-down and /sync-up before overwriting files. If no backup name is provided, the most recent backup is used. Does not modify the remote repository.
Configuration
Main config: ~/.config/opencode/opencode-sm.jsonc
Created automatically by /sync-new or /sync-connect.
{
// GitHub repository configuration
"repo": {
// "owner": "your-github-username",
// "name": "opencode-config",
// "branch": "main"
},
// Sync secrets (API keys, tokens, auth data).
// WARNING: Only enable if your sync repo is PRIVATE.
"includeSecrets": false,
// Extra paths to sync (files or directories).
"extraConfigPaths": [],
// Glob patterns to exclude from sync entirely.
// You can also use a .syncignore file in your config directory.
"excludePaths": [],
// Machine name used in git commits and machine isolation folder.
// Falls back to OS hostname if empty.
"machineName": "",
// AES-256-GCM encryption key for sensitive config values.
// Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
// Use the same key on all machines. Never commit this value.
// "encryptionKey": "your-64-hex-character-key-here"
}| Field | Type | Default | Description |
|-------|------|---------|-------------|
| repo.owner | string | gh api user | GitHub owner for the sync repo |
| repo.name | string | opencode-config | Name of the sync repo |
| repo.branch | string | Auto-detected | Git branch |
| includeSecrets | boolean | false | Sync blocked/secret files (requires private repo) |
| extraConfigPaths | string[] | [] | Extra file/directory paths to include |
| excludePaths | string[] | [] | Glob patterns to exclude from sync |
| machineName | string | OS hostname | Name used in commits and machine isolation |
| encryptionKey | string | — | AES-256-GCM key (hex, 32 bytes). Never synced. |
| safe | boolean | true | Safe mode — startup only logs divergence, never modifies |
Per-machine overrides: ~/.config/opencode/opencode-sm.overrides.jsonc
Never synchronized. Deep-merged into runtime config after every /sync-down.
{
// "model": "anthropic/claude-sonnet-4-5",
// "agent": { "build": { "permission": { "bash": "ask" } } }
}.syncignore
Create ~/.config/opencode/.syncignore with gitignore-style patterns to exclude files from sync. Merged with excludePaths from config.
# Example .syncignore
*.log
sensitive-local-only.json
agents/private-*.mdEncryption
When encryptionKey is configured, individual sensitive field values are encrypted with AES-256-GCM before being pushed to the remote repo, and decrypted transparently on pull.
- Each encrypted value is self-contained (stores IV + auth tag + ciphertext inline)
- Pass-through transparent — machines without a key read values as-is
- The key itself is in
SENSITIVE_KEYSand is never synced
Generate a key:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Set the same key in opencode-sm.jsonc on every machine that shares the repo.
Machine Isolation
When machineName is configured, each machine syncs to its own subfolder:
opencode-config/
machines/
work-pc/
agents/zeus.md
opencode.json
home-pc/
agents/ra.md
tui.json/sync-upwrites only tomachines/<machineName>//sync-downreads only frommachines/<machineName>/- Other machines' files are never touched
Without machineName, files go to the repo root (backward compatible).
Security
Secret sanitization
All files pass through a sanitizer before upload:
JSON files — deep key-based redaction of:
apiKey, api_key, apikey, token, password, secret, credential, authorization, privateKey, private_key, accessToken, access_token, refreshToken, refresh_token, clientSecret, client_secret
Non-JSON files — regex-based redaction of:
- OpenAI/Anthropic API keys (
sk-...) - GitHub tokens (
ghp_,gho_,ghu_,ghs_,ghr_prefixes) - Bearer and Authorization header values
- RSA / EC / OpenSSH private key blocks
Denylist (blocked by default)
| Pattern | Type |
|---------|------|
| auth.json | Credential store |
| mcp-auth.json | MCP credential store |
| opencode.db | Session database |
| .netrc | Login credentials |
| .git-credentials | Git credentials |
| .env | Environment variables |
| *.pem, *.key | Private key files |
| storage/session, storage/message, storage/part | Session data |
Only synced when includeSecrets: true is explicitly set.
Backups
Before any destructive write, a timestamped backup is created:
~/.local/share/opencode/opencode-sm/backups/backup-<timestamp>/Up to 10 write manifests are retained; older ones are rotated automatically. Use /sync-restore to recover.
Environment Variables
| Variable | Description |
|----------|-------------|
| OPENCODE_CONFIG_DIR | Override the OpenCode config directory |
| OPENCODE_CONFIG | Override the path to the main config file |
| GITHUB_TOKEN / GH_TOKEN | GitHub token (fallback: gh auth token) |
Directory Structure
| Purpose | Linux/macOS | Windows |
|---------|-------------|---------|
| Config | ~/.config/opencode/ | %APPDATA%\opencode\ |
| Data | ~/.local/share/opencode/ | %LOCALAPPDATA%\opencode\ |
| State | ~/.local/state/opencode/ | %LOCALAPPDATA%\opencode\state\ |
| Sync repo | ~/.local/share/opencode/opencode-sm/repo | — |
| Sync backups | ~/.local/share/opencode/opencode-sm/backups | — |
| Plugin config | ~/.config/opencode/opencode-sm.jsonc | — |
| Local overrides | ~/.config/opencode/opencode-sm.overrides.jsonc | — |
What gets synced
| Category | Paths | Sanitized? |
|----------|-------|------------|
| Main configs | opencode.json, tui.json, AGENTS.md | No |
| Agents | agents/, agent/ | Yes |
| Commands | commands/, command/ | Yes |
| Skills | skills/, skill/ | Yes |
| Tools | tools/, tool/ | Yes |
| Themes | themes/, theme/ | Yes |
| Plugins | plugins/, plugin/ | Yes |
| Modes | modes/, mode/ | Yes |
| Extra paths | extraConfigPaths | Yes |
| Auth/sessions | auth.json, .env, *.pem, etc. | Only with includeSecrets: true |
What is never synced
opencode-sm.overrides.jsonc— per-machine overridesopencode-sm.jsonc— plugin config (recreated per machine)encryptionKey— encryption key (local only)state.json— sync timestampsmodel.json,prompt-stash.jsonl,prompt-history.jsonl
Changelog
v0.3.3 — 2026-05-07
Event-driven startup & toast notifications
- Startup sync now triggers on
server.connectedevent instead ofsetTimeout— the TUI is guaranteed to be ready before any notification appears - Guard flag prevents double-run if
server.connectedfires more than once - Every sync command (
/sync-up,/sync-down,/sync-fix,/sync-connect,/sync-new,/sync-check,/sync-restore) now fires atui.showToastpopup with a descriptive summary - Variant (
success/warning/error/info) is inferred from the result heading automatically - Errors also surface as
error-variant toasts
v0.3.2 — 2026-05-07
Encryption, async git, conflict detection, log/restore commands
- AES-256-GCM encryption for sensitive config values — set
encryptionKeyinopencode-sm.jsonc; values are encrypted before push and decrypted on pull - All git/gh calls converted to async (
execFileAsync) with configurable timeouts — no more blocking the event loop - Proper JSONC parser — character-by-character parser replaces regex-based approach that was corrupting URLs inside strings
- Conflict detection — files changed both locally and remotely are preserved locally and logged; no silent overwrites
- New
/sync-logcommand — shows recent commit history from the sync repo - New
/sync-restorecommand — restores local config files from a backup .syncignoresupport — create~/.config/opencode/.syncignorefor fine-grained exclusions- Atomic file writes — files written via tmp+rename; write manifests saved with rotation (max 10)
- Machine name uniqueness check on
/sync-connect - Health check on startup — verifies git, gh CLI, and git identity before syncing
- Windows path fix —
dataandstatedirectories are now distinct on Windows BunShelltyped correctly viaPluginInput['$']
v0.3.1 — 2026-05-03
Rich UI, auto-pull on startup, toast notifications
- Rich markdown output for all commands with badges, tables, and section headers
fmtStartupPull,fmtDown,fmtUp,fmtFix,fmtCheck,fmtRepo,fmtInit,fmtConnectformatters- Auto-pull on startup in safe mode (read remote, apply non-conflicting changes)
tui.showToastnotifications for startup sync and command results- Expanded
SENSITIVE_KEYSlist for sanitization
v0.3.0 — 2026-05-01
Exclude paths, machine isolation, sync-fix, safer sync engine
excludePathsconfig field with glob pattern support- Machine isolation:
machineNameroutes files tomachines/<name>/in the repo - New
/sync-fixcommand to propagate pending local deletions - Safe mode (
safe: truedefault) — startup never modifies files - Bidirectional conflict detection
- Per-machine overrides (
opencode-sm.overrides.jsonc)
v0.2.0
Initial public release
- GitHub-based sync via
ghCLI /sync-new,/sync-connect,/sync-up,/sync-down,/sync-check,/sync-repo- Secret sanitization (JSON + regex)
- Automatic local backups
extraConfigPathssupportincludeSecretsopt-in for private repos
License
MIT
