Skip to content

Releases: lazyalex616/opencode-github-sync

v3.2.0 — one-line install, bilingual menu, OS-scheduled autosync

Choose a tag to compare

@github-actions github-actions released this 26 Oct 07:09
507f52f

English

Added

  • One-line installers. install.ps1 (Windows PowerShell 5.1 and PowerShell 7) and install.sh (macOS, Linux) check for Node.js 18+ and git, offer to install a missing one through winget, Homebrew or the system package manager (they ask first and never install silently), run npm install -g opencode-github-sync@latest, and open the menu. --mode, --repo, --interval, --lang, --proxy and --yes run the whole setup non-interactively. Under curl | sh questions are read from the terminal, and on Windows the menu runs as its own process so it gets the keyboard.
  • Interactive menu. opencode-sync with no arguments (or opencode-sync menu) opens a menu with arrow keys, hotkeys and checkboxes, and falls back to numbered prompts whenever input is not a terminal. Every existing subcommand still works and stays scriptable. First run offers: install the command only, link a repository, or link and make this the primary device with autosync. When the machine and the repository differ, it shows the differences and lets you pick a side; the losing side's files go to a recovery folder.
  • English and Simplified Chinese for every menu and command string, detected from OPENCODE_SYNC_LANG, LC_ALL/LC_MESSAGES/LANG or the system locale, and switchable with --lang or from the menu.
  • Autosync driven by the operating system. opencode-sync autosync install|uninstall|enable|disable|status|run|reset|pause|resume. Task Scheduler on Windows (logon, unlock, remote connect and a repeating timer; hidden through conhost --headless or a UTF-16 VBScript launcher), launchd on macOS, a systemd user timer on Linux with a crontab fallback. Each run pushes only when the synced trees have been quiet for a settle period, and otherwise receives only when this machine has nothing of its own in flight. Machines are staggered so they do not collide, failures back off from 30 to 60 minutes without ever giving up, a lost push race is retried rather than counted, and a desktop notification appears only when backing off starts and when it recovers.
  • What to sync as checkboxes: main config, instruction files, agents, commands, plugins, skills, credentials and sessions. A switched-off category stays local in both directions.
  • Large-change guard. A push that adds more than 300 files, more than 10 MB, or any file over 2 MB waits for confirmation (thresholds are adjustable). Autosync never confirms: it records a failure and notifies. The first push into an empty repository carries the whole existing setup, so only the single-file limit applies there. Generated directories (node_modules, .venv, venv, __pycache__, dist, .cache) are never staged.
  • Restore a file from history, with the replaced version kept in the recovery folder.
  • Device list built from commit history, and renaming this machine.
  • Network proxy for git, gh and npm, applied to child processes only.
  • Preview of what a sync would change before running it.
  • Support bundle export with tokens, passwords and URL credentials removed.
  • Update menu: check npm, upgrade, and an optional daily auto-update run by the autosync job.

Changed

  • When autosync is on, the plugin no longer pushes on idle (it still pulls at startup), so it never races the scheduled job.
  • Sync commits carry an Opencode-Sync-Device trailer naming the machine.
  • link accepts any git URL or local path in addition to owner/name.
  • A forced pull, and the first pull onto a machine that already has files, copy the files they replace into opencode-sync-recovery under the data directory first. This covers new folders, non-ASCII file names, and local skills and extra-path files that the forced pull removes or overwrites.

Fixed

  • A push that loses a race with another machine rebases and retries up to three times instead of failing. A protected branch or a declined hook is reported as a failure rather than retried.
  • The first push from a machine whose config directory is not yet a repository now writes the remote's skills out before staging, so it can no longer publish every skill it lacks as a deletion.
  • Git runs with core.quotepath=off and English messages, so non-ASCII file names count toward the large-change guard and a lost push race is recognised on a localized git.

简体中文

新增

  • 一行命令安装。 install.ps1(Windows PowerShell 5.1 和 PowerShell 7 都能用)和 install.sh(macOS、Linux)会检查 Node.js 18+ 和 git,缺了就提示用 winget、Homebrew 或系统包管理器安装(一定先问,绝不偷偷装),然后执行 npm install -g opencode-github-sync@latest 并打开菜单。带上 --mode、--repo、--interval、--lang、--proxy、--yes 可以全程无人值守完成设置。用 curl | sh 时问题从终端读取;在 Windows 上菜单作为独立进程启动,键盘输入不会被管道吃掉。
  • 交互菜单。 直接运行 opencode-sync(或 opencode-sync menu)会打开菜单,支持方向键、快捷键和勾选框;输入不是终端时自动改用编号提问。原有子命令全部保留,照样可以写进脚本。第一次运行时可以选:只装命令、关联仓库、关联并把本机设为开启自动同步的主力设备。本机和仓库内容不一样时会先列出差异让你选以哪边为准,被替换的一方会先存进恢复目录。
  • 英文和简体中文界面,覆盖所有菜单和命令输出。语言按 OPENCODE_SYNC_LANG、LC_ALL/LC_MESSAGES/LANG、系统区域设置依次判断,也可以用 --lang 或在菜单里切换。
  • 由操作系统驱动的自动同步。 新增 opencode-sync autosync install|uninstall|enable|disable|status|run|reset|pause|resume。Windows 用任务计划程序(登录、解锁、远程连接时触发,外加定时重复;通过 conhost --headless 或 UTF-16 编码的 VBScript 隐藏窗口),macOS 用 launchd,Linux 用 systemd 用户定时器,不可用时退回 crontab。每次运行只在同步目录安静一段时间后才推送;还有文件在变化时,只有本机没有待处理的改动才会接收。多台机器会错开时间避免撞车;连续失败后退避间隔从 30 分钟加到 60 分钟,但永不放弃;推送被别的机器抢先不算失败,会自动重试;桌面通知只在开始退避和恢复正常时各弹一次。
  • 同步内容勾选:主配置、指令文件、智能体、自定义命令、插件、技能、登录凭据、会话。关掉的类别双向都只留在本机。
  • 大改动保护。 一次推送新增超过 300 个文件、总量超过 10 MB,或有单个文件超过 2 MB 时,要先确认(阈值可调)。自动同步从不自动确认,只记一次失败并发通知。往空仓库的第一次推送本来就要带上全部现有配置,所以只检查单个文件大小。node_modules、.venv、venv、__pycache__、dist、.cache 这类生成目录永远不会提交。
  • 从历史版本恢复单个文件,被替换的版本保存在恢复目录里。
  • 设备列表,根据提交历史显示每台机器上次同步的时间,并可以给本机改名。
  • 网络代理,只作用于 opencode-sync 启动的 git、gh 和 npm。
  • 同步前预览会改动哪些文件。
  • 导出排障包,自动去掉令牌、密码和 URL 里的账号密码。
  • 更新菜单:检查 npm 新版本、立即升级,以及可选的每日自动更新(由自动同步任务执行)。

变更

  • 开启自动同步后,插件不再在会话空闲时推送(启动时仍然拉取),避免和定时任务抢着同步。
  • 同步提交带上 Opencode-Sync-Device 尾注,记录是哪台机器提交的。
  • link 除了 用户名/仓库名,也接受任意 git 地址或本地路径。
  • 强制拉取,以及本机已有文件时的第一次拉取,会先把将被替换的文件复制到数据目录下的 opencode-sync-recovery。新建的整个文件夹、中文等非 ASCII 文件名,以及强制拉取会删除或覆盖的本机技能和额外路径文件,也都包括在内。

修复

  • 推送时被别的机器抢先,会自动变基并最多重试三次,而不是直接失败。分支受保护或被钩子拒绝时会如实报错,不会反复重试。
  • 配置目录还不是仓库的机器第一次推送时,会先把仓库里的技能写回本机再打包,不会再把本机缺少的技能当成删除推送出去。
  • git 改为以 core.quotepath=off 和英文提示运行:中文文件名会正确计入大改动保护,非英文系统上也能识别推送被抢先的情况。

Install

npm install -g opencode-github-sync@3.2.0

Plugin users: OpenCode picks up the new version on its next start.

Contributors

Thank you to everyone who made this release possible:

  • @vitaly74 — OpenCode 2 plugin entrypoint (#6, 3.1.0)
  • @aquelejota — deterministic shard export and working session tests (#5), analysis of #2, #3 and #4 (3.1.0)
  • @logser13 — reported #2, #3 and #4 with reproductions and tested fixes (3.1.x)

What's Changed

  • 3.2.0: installers, bilingual menu, OS-scheduled autosync by @doomsday616 in #7

Full Changelog: v3.1.1...v3.2.0

v3.1.1 — OpenCode 2 support and sync fixes

Choose a tag to compare

@github-actions github-actions released this 26 Oct 03:34

This release rolls up everything since 3.0.1: the community fixes and OpenCode 2 support from 3.1.0, plus the 3.1.1 sync fixes.

本版本汇总了 3.0.1 之后的全部改动:3.1.0 的社区修复和 OpenCode 2 支持,以及 3.1.1 的同步修复。

English

3.1.1

Fixed

  • A push that rebased onto other machines' commits now writes the merged result out to the real directories (~/.agents/skills, state, data, extra paths). Previously the new files landed only in the repository, a later pull saw nothing incoming and skipped the write-out, and the next push staged the stale local tree and deleted another machine's new skills. A push with nothing local now fast-forwards and writes out the same way.
  • The commit last written out is recorded in .git/info/opencode-sync-stage-out. When HEAD has moved past it (for example a push that stopped after the rebase), the next push or pull writes the repository out before doing anything else, so the gap can no longer be published as deletions.
  • An extraPaths source that does not exist on this machine no longer deletes the shared repository copy. It is skipped and reported; other machines keep receiving it.
  • Writing out a directory extraPaths entry no longer deletes local files that are not in the repository copy, so a tool's runtime files beside its synced config survive a pull. --force still makes it an exact copy. File entries continue to replace only that file.

3.1.0

Added

  • OpenCode 2 support. One package now loads on both OpenCode 1.x and 2.x. Thanks @vitaly74 (#6). How it works:
    • The default export is a single object that satisfies both loaders. OpenCode 2 looks for { id, setup }: setup(ctx) registers the opencode_sync tool through ctx.tool.transform, subscribes to session.idle through ctx.event.subscribe for the optional idle push, and runs the startup pull. OpenCode 1.x (1.3.4 and later) sees the same object's id and server() and calls server() once, so the plugin is registered exactly once.
    • No dependency on @opencode/plugin; the V2 context is used structurally.
    • Config key. OpenCode 1.x reads only "plugin" and silently drops "plugins". OpenCode 2 reads its native "plugins" and still accepts the legacy "plugin". So "plugin": ["opencode-github-sync"] is the one form that works on both, and it is what the README recommends. A 2.x-only setup may use "plugins", but never list the plugin under both keys: 2.x loads both lists.
    • Plugin cache pruning reads both keys, so a mixed fleet where some machines are on 1.x and some on 2.x keeps every plugin either version needs.
    • OpenCode 1.0–1.3.3 iterate every export and call each as a function, which the new object default export cannot satisfy. Stay on 3.0.1 there.

Fixed

  • Session import no longer cascade-deletes sessions. INSERT OR REPLACE deletes the conflicting row before inserting, so replacing a shared project row cascaded through ON DELETE CASCADE and removed every session imported before it. It is replaced by a real INSERT ... ON CONFLICT DO UPDATE upsert that never deletes and keeps newer-time_updated-wins per row. Reported with a tested fix by @logser13, cascade analysis by @aquelejota (#2).
  • The synthetic global project is machine-specific, so it is no longer exported, and importing an older shard that carries it never overwrites the local row. Reported by @logser13, confirmed by @aquelejota (#4).
  • Session shard export is deterministic: an unchanged session produces a byte-identical shard, so it no longer shows up as a change on every push. Reported by @logser13 (#3), fixed by @aquelejota (#5).
  • The session tests now actually run under vitest; previously node:sqlite failed to load there and every session test returned early. Found and fixed by @aquelejota (#5).
  • Plugin cache pruning reads both the plugin and plugins keys and understands [spec, options] tuples and { package } objects, so a plugin listed in either form is no longer pruned from the cache. Follow-up to @vitaly74's #6.

Changed

  • The minimum supported OpenCode 1.x is now 1.3.4. OpenCode 1.0–1.3.3 cannot load this release; stay on 3.0.1 there.

简体中文

3.1.1

修复

  • 推送时如果先合并了其他机器的提交,现在会把合并后的结果写回本机的真实目录(~/.agents/skills、state、data、额外路径)。以前这些新文件只进了仓库,之后的拉取发现没有新提交就跳过写回,下一次推送又把本机过时的目录打包上去,结果把别的机器刚装的 skill 删掉了。本机没有改动的推送现在会直接快进,并用同样的方式写回。
  • 最近一次写回的提交记录在 .git/info/opencode-sync-stage-out。如果 HEAD 已经越过这个记录(例如推送在合并之后中断),下一次推送或拉取会先把仓库写回本机,再做其他事,这段差异不会再被当成删除推送出去。
  • extraPaths 里的文件在本机不存在时,不再删除仓库里的共享副本,而是跳过并提示,其他机器照常接收。
  • 写回目录类型的 extraPaths 时,不再删除仓库副本里没有的本机文件,同步配置旁边的工具运行文件在拉取后能保留下来。--force 仍然会做成完全一致的副本。文件类型的条目仍然只替换那一个文件。

3.1.0

新增

  • 支持 OpenCode 2。 同一个包现在在 OpenCode 1.x 和 2.x 上都能加载。感谢 @vitaly74(#6)。原理如下:
    • 默认导出是一个同时满足两种加载方式的对象。OpenCode 2 找的是 { id, setup }:setup(ctx) 通过 ctx.tool.transform 注册 opencode_sync 工具,通过 ctx.event.subscribe 监听 session.idle 以支持可选的空闲推送,并在启动时拉取一次。OpenCode 1.x(1.3.4 及以上)看到的是同一个对象上的 id 和 server(),只调用一次 server(),所以插件只注册一次,不会重复。
    • 不依赖 @opencode/plugin,V2 的上下文按结构直接使用。
    • 配置写法。 OpenCode 1.x 只读 "plugin",遇到 "plugins" 会直接丢掉且不报错;OpenCode 2 读自己的新写法 "plugins",同时也认旧的 "plugin"。所以 "plugin": ["opencode-github-sync"] 是两个版本都能用的唯一写法,README 也推荐这样写。只用 2.x 的话可以写 "plugins",但不要两个键里都写这个插件,2.x 会把两个列表都加载一遍。
    • 清理插件缓存时同时读取两个键,有的机器在 1.x、有的在 2.x 时,两个版本需要的插件都不会被误删。
    • OpenCode 1.0–1.3.3 会把每个导出都当函数调用,新的对象形式默认导出满足不了,这些版本请继续使用 3.0.1。

修复

  • 导入会话不再连带删除其他会话。INSERT OR REPLACE 会先删掉冲突的行再插入,替换共享的 project 行时会通过 ON DELETE CASCADE 把之前导入的会话全部删掉。现在改用真正的 INSERT ... ON CONFLICT DO UPDATE,从不删除行,并且按行保留 time_updated 较新的一方。由 @logser13 报告并附上验证过的修法,@aquelejota 补充了连带删除的分析(#2)。
  • 合成的 global 项目属于每台机器自己,不再导出;导入带有它的旧分片时,也不会覆盖本机的那一行。由 @logser13 报告,@aquelejota 确认(#4)。
  • 会话分片导出现在是确定的:没有变化的会话生成的分片逐字节相同,不会在每次推送时都显示为改动。由 @logser13 报告(#3),@aquelejota 修复(#5)。
  • 会话测试现在真的会在 vitest 下运行。以前 node:sqlite 在那里加载失败,每个会话测试都提前返回。由 @aquelejota 发现并修复(#5)。
  • 清理插件缓存时同时读取 plugin 和 plugins 两个键,并识别 [spec, options] 元组和 { package } 对象,无论用哪种写法列出的插件都不会再被误删缓存。这是对 @vitaly74 #6 的补充修改。

变更

  • OpenCode 1.x 的最低支持版本变为 1.3.4。OpenCode 1.0–1.3.3 无法加载此版本,请继续使用 3.0.1。

Install

npm install -g opencode-github-sync@3.1.1

Plugin users: OpenCode picks up the new version on its next start.

Contributors

Thank you to everyone who made this release possible:

What's Changed

  • fix: make session shard export deterministic by @aquelejota in #5
  • Add OpenCode 2 plugin entrypoint (dual V1+V2 export) by @vitaly74 in #6

New Contributors

Full Changelog: v3.0.1...v3.1.1

v3.1.0

Choose a tag to compare

@github-actions github-actions released this 26 Oct 03:01

English

Added

  • OpenCode 2 support. One package now loads on both OpenCode 1.x and 2.x. Thanks @vitaly74 (#6). How it works:
    • The default export is a single object that satisfies both loaders. OpenCode 2 looks for { id, setup }: setup(ctx) registers the opencode_sync tool through ctx.tool.transform, subscribes to session.idle through ctx.event.subscribe for the optional idle push, and runs the startup pull. OpenCode 1.x (1.3.4 and later) sees the same object's id and server() and calls server() once, so the plugin is registered exactly once.
    • No dependency on @opencode/plugin; the V2 context is used structurally.
    • Config key. OpenCode 1.x reads only "plugin" and silently drops "plugins". OpenCode 2 reads its native "plugins" and still accepts the legacy "plugin". So "plugin": ["opencode-github-sync"] is the one form that works on both, and it is what the README recommends. A 2.x-only setup may use "plugins", but never list the plugin under both keys: 2.x loads both lists.
    • Plugin cache pruning reads both keys, so a mixed fleet where some machines are on 1.x and some on 2.x keeps every plugin either version needs.
    • OpenCode 1.0–1.3.3 iterate every export and call each as a function, which the new object default export cannot satisfy. Stay on 3.0.1 there.

Fixed

  • Session import no longer cascade-deletes sessions. INSERT OR REPLACE deletes the conflicting row before inserting, so replacing a shared project row cascaded through ON DELETE CASCADE and removed every session imported before it. It is replaced by a real INSERT ... ON CONFLICT DO UPDATE upsert that never deletes and keeps newer-time_updated-wins per row. Reported with a tested fix by @logser13, cascade analysis by @aquelejota (#2).
  • The synthetic global project is machine-specific, so it is no longer exported, and importing an older shard that carries it never overwrites the local row. Reported by @logser13, confirmed by @aquelejota (#4).
  • Session shard export is deterministic: an unchanged session produces a byte-identical shard, so it no longer shows up as a change on every push. Reported by @logser13 (#3), fixed by @aquelejota (#5).
  • The session tests now actually run under vitest; previously node:sqlite failed to load there and every session test returned early. Found and fixed by @aquelejota (#5).
  • Plugin cache pruning reads both the plugin and plugins keys and understands [spec, options] tuples and { package } objects, so a plugin listed in either form is no longer pruned from the cache. Follow-up to @vitaly74's #6.

Changed

  • The minimum supported OpenCode 1.x is now 1.3.4. OpenCode 1.0–1.3.3 cannot load this release; stay on 3.0.1 there.

简体中文

新增

  • 支持 OpenCode 2。 同一个包现在在 OpenCode 1.x 和 2.x 上都能加载。感谢 @vitaly74(#6)。原理如下:
    • 默认导出是一个同时满足两种加载方式的对象。OpenCode 2 找的是 { id, setup }:setup(ctx) 通过 ctx.tool.transform 注册 opencode_sync 工具,通过 ctx.event.subscribe 监听 session.idle 以支持可选的空闲推送,并在启动时拉取一次。OpenCode 1.x(1.3.4 及以上)看到的是同一个对象上的 id 和 server(),只调用一次 server(),所以插件只注册一次,不会重复。
    • 不依赖 @opencode/plugin,V2 的上下文按结构直接使用。
    • 配置写法。 OpenCode 1.x 只读 "plugin",遇到 "plugins" 会直接丢掉且不报错;OpenCode 2 读自己的新写法 "plugins",同时也认旧的 "plugin"。所以 "plugin": ["opencode-github-sync"] 是两个版本都能用的唯一写法,README 也推荐这样写。只用 2.x 的话可以写 "plugins",但不要两个键里都写这个插件,2.x 会把两个列表都加载一遍。
    • 清理插件缓存时同时读取两个键,有的机器在 1.x、有的在 2.x 时,两个版本需要的插件都不会被误删。
    • OpenCode 1.0–1.3.3 会把每个导出都当函数调用,新的对象形式默认导出满足不了,这些版本请继续使用 3.0.1。

修复

  • 导入会话不再连带删除其他会话。INSERT OR REPLACE 会先删掉冲突的行再插入,替换共享的 project 行时会通过 ON DELETE CASCADE 把之前导入的会话全部删掉。现在改用真正的 INSERT ... ON CONFLICT DO UPDATE,从不删除行,并且按行保留 time_updated 较新的一方。由 @logser13 报告并附上验证过的修法,@aquelejota 补充了连带删除的分析(#2)。
  • 合成的 global 项目属于每台机器自己,不再导出;导入带有它的旧分片时,也不会覆盖本机的那一行。由 @logser13 报告,@aquelejota 确认(#4)。
  • 会话分片导出现在是确定的:没有变化的会话生成的分片逐字节相同,不会在每次推送时都显示为改动。由 @logser13 报告(#3),@aquelejota 修复(#5)。
  • 会话测试现在真的会在 vitest 下运行。以前 node:sqlite 在那里加载失败,每个会话测试都提前返回。由 @aquelejota 发现并修复(#5)。
  • 清理插件缓存时同时读取 plugin 和 plugins 两个键,并识别 [spec, options] 元组和 { package } 对象,无论用哪种写法列出的插件都不会再被误删缓存。这是对 @vitaly74 #6 的补充修改。

变更

  • OpenCode 1.x 的最低支持版本变为 1.3.4。OpenCode 1.0–1.3.3 无法加载此版本,请继续使用 3.0.1。

Install

npm install -g opencode-github-sync@3.1.0

Plugin users: OpenCode picks up the new version on its next start.

Contributors

Thank you to everyone who made this release possible:

What's Changed

  • fix: make session shard export deterministic by @aquelejota in #5
  • Add OpenCode 2 plugin entrypoint (dual V1+V2 export) by @vitaly74 in #6

New Contributors

Full Changelog: v3.0.1...v3.1.0

v3.0.1

Choose a tag to compare

@github-actions github-actions released this 29 Jul 04:56

English

Fixed

  • status no longer reports the config file as uncommitted work on machines that use per-machine overrides. The file on disk is supposed to differ from the committed version once overrides are in play, so plain git status flagged it as modified forever — a warning the user could never clear. The config is now compared with overrides subtracted, so only genuine edits count.
  • git status --porcelain output is parsed by pattern rather than at a fixed column offset. Output is trimmed before parsing, which removed the leading column of an unstaged entry and shifted every field after it, silently yielding a truncated filename.

简体中文

修复

  • 在使用本机覆盖的机器上,status 不再把配置文件报告为未提交的改动。启用覆盖后,磁盘上的文件本来就应该和提交的版本不同,所以单纯的 git status 会永远提示已修改,用户无法消除。现在会先减去覆盖再比较,只有真正的编辑才算改动。
  • 按模式解析 git status --porcelain 的输出,而不是按固定列位置。以前输出在解析前被裁掉了首部空白,未暂存条目的第一列丢失,后面的字段整体错位,得到的文件名被悄悄截断。

Install

npm install -g opencode-github-sync@3.0.1

Plugin users: OpenCode picks up the new version on its next start.

Full Changelog: v3.0.0...v3.0.1

v3.0.0 — plugin, overrides, selective session sync

Choose a tag to compare

@lazyalex616 lazyalex616 released this 29 Jul 03:23

Complete rewrite. The published scripts had fallen four months behind the version in daily use, and the whole-database session sync they shipped stops being viable once opencode.db passes a gigabyte.

npm install -g opencode-github-sync

Highlights

OpenCode plugin. Startup pull, optional push on idle, and an opencode_sync tool. The CLI stays, because a plugin cannot repair the configuration that stops the plugin from loading.

Per-machine overrides. opencode-sync.overrides.jsonc is merged into the config after a pull and subtracted again before a push, using git show HEAD: as the baseline rather than the working copy. An overridden key is invisible to sync in both directions, and the machine keeps its own settings throughout.

Selective session sync. Sessions export individually as gzipped JSON shards, chosen by time window, include list, project directory and per-session size cap. Different sessions are different files, so git merges them unaided; the same session edited twice resolves by time_updated. Import is a transactional upsert.

One-command setup. opencode-sync init creates the private repository and configures the machine through the GitHub CLI.

Correctness

  • Pushes are verified by reading the remote head back — git push can exit zero without the remote moving.
  • Symlinks and Windows junctions are refused rather than followed.
  • File and directory replacements are atomic.
  • A cross-process lock covers the plugin, the CLI and every OpenCode window.
  • Line endings pinned to LF; text=auto alone rewrote skill shell scripts to CRLF on Windows.
  • Commit messages use a stable pseudonym, so a corporate asset-tag hostname cannot reach the repository.
  • Credentials are opt-in and refused on public repositories.

Breaking changes

Session sync no longer commits the database, and Git LFS is no longer used. The shell and .cmd wrappers are replaced by a real bin entry — install from npm instead of copying scripts.

Full notes in CHANGELOG.md.

v2.1.0 — Tokyo Night UI + Bug Fixes

Choose a tag to compare

@lazyalex616 lazyalex616 released this 26 Mar 06:25

What's New in v2.0 → v2.1

🎨 Complete UI Redesign — Tokyo Night Theme

The entire CLI has been redesigned with a Tokyo Night color palette using true color RGB and ANSI 256:

  • No-box header: OpenCode Sync — ⚡ Push to GitHub (clean, no border)
  • Force mode shown inline as ⚠ FORCE in red
  • Interactive menu via @inquirer/select with ○/● circle indicators and cyan rgb(92,156,245) highlight
  • Braille spinner (⠋⠙⠹...) during sync operations, clears on first real output
  • Step-by-step streaming output: Each phase appears as it completes with file-level detail
  • Completion banner: Cyan ━━━━━ separator + green ✨ 同步完成! + descriptive slate text on the same line

📝 Chinese UI Text

All user-facing text is now in Chinese. Git terminology (Push, Pull, Fetch, Stash, Rebase, Commit, Reset) is kept in English for clarity.

🧭 4-Option Interactive Menu

? 你想同步什么?
❯ ● 仅配置
  ○ 仅会话
  ○ 全部
  ○ 退出

After selection, the full menu is redrawn with the selected item highlighted in cyan ● and others dimmed in gray ○.

🚀 Bootstrap Launcher

opencode-launcher.js sits between wrapper scripts and the sync core:

  • Self-update: Fetches remote and updates scripts/ if changed
  • Dependency bootstrap: Auto-installs @inquirer/select when needed
  • Delegation: All arguments pass through transparently

🐛 Bug Fixes (v2.1)

  • Fix data loss on pull when already up-to-date — doPull previously called stageOut even when no remote changes existed, which ran rmDirRecursive on local directories and deleted files not yet pushed. Now stageOut is only called when there are actual incoming changes.
  • Fix banner accuracy — Banner is now printed by the child scripts (sync-config/sync-sessions) instead of dispatch, so "已是最新" shows upToDateBanner and actual syncs show completionBanner.
  • Fix --target label propagation — When running "both", each child script gets its own --target=config / --target=sessions for accurate banner text.
  • Add SYNC_CONFIG_ROOT / SYNC_DATA_ROOT env overrides to sync-sessions.js — Previously only sync-config.js supported these; now both are consistent.
  • Add skipLfs() to sync-sessions.js — Matches sync-config.js behavior.
  • Fix color inconsistency — c.gray → c.tn.gray throughout.
  • Fix English messages in sync-sessions.js — initRepo step messages changed to Chinese.

🧪 Test Suite (v2.1)

New tests/test-sync-logic.js with 34 integration tests covering:

  • Push with changes / no changes
  • Pull with incoming / no changes
  • BUG1 regression: local new files preserved on pull-no-changes
  • stageOut called correctly on pull-with-changes
  • Banner target label accuracy (config / sessions / both)
  • No double banner from dispatch

🎨 Color Reference

Element Color Code
Titles, accents, selected item, separator Blue/cyan rgb(92,156,245)
"同步完成!" text Green rgb(0,210,106)
Success checkmark ✅ emoji —
Warnings Yellow #e0af68 (ANSI 179)
Errors, FORCE tag Red #f7768e (ANSI 204)
⚡ icon Purple #9d7cd8 (ANSI 140)
Borders, dim, unselected Gray #565f89 (ANSI 60)
Body text Text #a9b1d6 (ANSI 146)
Banner description Slate #94a3b8 (ANSI 103)

Terminal Output

  OpenCode Sync — ⚡ Push to GitHub  ⚠ FORCE

? 你想同步什么?
  ❯ ● 仅配置
    ○ 仅会话
    ○ 全部
    ○ 退出

  ✅  正在从 4 个目录暂存文件...  📝~5
       ~ _state/frecency.json
       ~ _state/kv.json
       + _agents/new-skill/SKILL.md
  ✅  已提交:5 个文件变更
  ✅  已 Push 到 GitHub repo(rebase,无冲突)

  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  ✨ 同步完成!  配置文件已成功 Push

📦 File Inventory

File Purpose
scripts/ui.js Shared UI — Tokyo Night RGB colors, logo, banners, formatStats
scripts/opencode-sync-core.js Menu, streaming runner, dispatch
scripts/sync-config.js Config push/pull with step-by-step output
scripts/sync-sessions.js Session push/pull with SQLite safety
scripts/opencode-launcher.js Bootstrap launcher with self-update
tests/test-sync-logic.js 34 integration tests

Breaking Changes

  • Wrapper scripts call opencode-launcher.js instead of opencode-sync-core.js directly. Update your wrapper files (copy from wrappers/ to ~/.local/bin/).
  • @inquirer/select is a required dependency. The launcher auto-installs it.

Full Changelog

v1.0.0...v2.1.0: v1.0.0...v2.1.0

v1.0.0 — Initial Release

Choose a tag to compare

@lazyalex616 lazyalex616 released this 18 Mar 16:23

Cross-device OpenCode config & session sync via a private GitHub repository.

What's included

  • 3 core scripts — opencode-sync-core.js, sync-config.js, sync-sessions.js
  • 8 wrapper commands — 4 for macOS/Linux, 4 for Windows (.cmd)
  • Bilingual docs — English + 中文

Features

  • Sync 4 directories (config, data, state, agents/skills) into one git repo
  • Interactive mode: choose config / sessions / both
  • Force push/pull for conflict resolution
  • Git LFS for large SQLite databases
  • SQLite safe backup via .backup command
  • Migration auto-repair across different OpenCode versions
  • Auto-stash local changes during pull

Getting started

  1. Clone this repo
  2. Copy scripts to ~/.config/opencode/scripts/
  3. Install wrappers to ~/.local/bin/
  4. Set SYNC_REMOTE_URL env var to your private GitHub repo
  5. Run opencode-push or opencode-pull