Skip to content

Commit 48ca5ac

Browse files
committed
feat!: rewrite as a typed plugin + CLI with overrides and selective session sync
The published scripts had fallen four months behind the version actually in daily use, and the whole-database session sync they shipped stops being viable once opencode.db passes a gigabyte. This replaces both. Architecture - One core, two entry points: an OpenCode plugin and an opencode-sync CLI. The CLI stays because a plugin cannot repair the configuration that prevents the plugin from loading. - Rewritten in TypeScript and published to npm as opencode-github-sync. Per-machine overrides - opencode-sync.overrides.jsonc is deep-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 therefore invisible to sync in both directions, and the machine keeps its own settings throughout. - Without an overrides file the config is copied byte for byte, so comments and formatting survive untouched. Selective session sync - Sessions are exported 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 ime_updated. Import is a transactional upsert. - The internal �vent log is excluded: largest table by row count, irrelevant to resuming a conversation. 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 are pinned to LF; ext=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. 82 tests run against a real bare git repository simulating two machines, on Node 20/22/24 across Linux, macOS and Windows.
1 parent 800bad0 commit 48ca5ac

58 files changed

Lines changed: 7943 additions & 3516 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitattributes

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Keep the source tree LF everywhere so a Windows checkout does not produce
2+
# spurious whole-file diffs.
3+
* text=auto eol=lf
4+
*.png binary
5+
*.svg text eol=lf

.github/workflows/ci.yml

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
workflow_dispatch:
8+
9+
concurrency:
10+
group: ${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
check:
15+
name: ${{ matrix.os }} · node ${{ matrix.node }}
16+
runs-on: ${{ matrix.os }}
17+
strategy:
18+
fail-fast: false
19+
matrix:
20+
# Windows and macOS are both first-class targets, and the path,
21+
# line-ending and process-signal behaviour genuinely differs between
22+
# them — so both are tested rather than assumed.
23+
os: [ubuntu-latest, windows-latest, macos-latest]
24+
# Node 22 is the oldest release with a built-in SQLite driver, so the
25+
# session tests only have full coverage from there upwards.
26+
node: [20, 22, 24]
27+
28+
steps:
29+
- uses: actions/checkout@v4
30+
31+
- uses: actions/setup-node@v4
32+
with:
33+
node-version: ${{ matrix.node }}
34+
cache: npm
35+
36+
- name: Configure git identity
37+
run: |
38+
git config --global user.name "CI"
39+
git config --global user.email "ci@example.com"
40+
git config --global init.defaultBranch main
41+
42+
- run: npm ci
43+
44+
- run: npm run lint
45+
46+
- run: npm run typecheck
47+
48+
- run: npm test
49+
50+
- run: npm run build
51+
52+
package:
53+
name: package contents
54+
runs-on: ubuntu-latest
55+
needs: check
56+
steps:
57+
- uses: actions/checkout@v4
58+
- uses: actions/setup-node@v4
59+
with:
60+
node-version: 22
61+
cache: npm
62+
- run: npm ci
63+
- run: npm run build
64+
- name: Verify the published tarball
65+
run: |
66+
npm pack --dry-run
67+
# The plugin and CLI entry points must both survive packing.
68+
test -f dist/plugin/index.js
69+
test -f dist/cli/index.js
70+
test -f dist/core/index.js

.github/workflows/release.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags: ["v*"]
6+
workflow_dispatch:
7+
8+
permissions:
9+
contents: write
10+
id-token: write
11+
12+
jobs:
13+
publish:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v4
17+
18+
- uses: actions/setup-node@v4
19+
with:
20+
node-version: 22
21+
registry-url: https://registry.npmjs.org
22+
cache: npm
23+
24+
- run: npm ci
25+
- run: npm run check
26+
- run: npm run build
27+
28+
- name: Publish to npm
29+
run: npm publish --provenance --access public
30+
env:
31+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
32+
33+
- name: Create the GitHub release
34+
uses: softprops/action-gh-release@v2
35+
with:
36+
generate_release_notes: true

.gitignore

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
node_modules/
2-
scripts/node_modules/
2+
dist/
3+
coverage/
4+
*.tsbuildinfo
35
.DS_Store
46
Thumbs.db
5-
desktop.ini
7+
.vitest-tmp/

CHANGELOG.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# Changelog
2+
3+
## 3.0.0
4+
5+
Complete rewrite. The previous release was a set of shell-wrapped Node scripts;
6+
this is a typed library with two entry points and a real test suite.
7+
8+
### Added
9+
- **OpenCode plugin.** Startup pull, optional push on idle, and an
10+
`opencode_sync` tool. The CLI remains, because a plugin cannot repair the
11+
configuration that stops the plugin from loading.
12+
- **Per-machine overrides.** `opencode-sync.overrides.jsonc` is deep-merged into
13+
the config after every pull and subtracted again before every push, so an
14+
overridden key is invisible to sync in both directions.
15+
- **Selective session sync.** Sessions are exported one at a time as gzipped
16+
JSON shards, selected by time window, explicit include list, project directory
17+
and per-session size cap. Import is a transactional upsert; the newer
18+
`time_updated` wins.
19+
- `opencode-sync init` / `link` for one-command setup through the GitHub CLI.
20+
- `--dry-run` on push and pull.
21+
- `extraPaths` for syncing arbitrary additional files.
22+
- Cross-platform CI on Node 20/22/24 across Linux, macOS and Windows.
23+
24+
### Changed
25+
- Rewritten in TypeScript, published to npm as `opencode-github-sync`.
26+
- New terminal UI with colour that degrades correctly for pipes, `NO_COLOR`
27+
and non-truecolor terminals.
28+
- Commit messages now use a stable pseudonym rather than the raw hostname, so a
29+
corporate asset tag can never reach the repository.
30+
- Line endings are pinned to LF in the worktree; `text=auto` alone was rewriting
31+
shell scripts inside skills to CRLF on Windows.
32+
- Credential syncing is opt-in and refused on public repositories.
33+
34+
### Removed
35+
- Whole-database session sync and the Git LFS dependency. Committing
36+
`opencode.db` stops being viable once it passes a gigabyte, and copying it
37+
alongside a live write-ahead log risks capturing torn data.
38+
- The shell/`.cmd` wrapper scripts, replaced by a real `bin` entry.
39+
40+
### Fixed
41+
- A push is now verified by reading the remote head back; `git push` can exit
42+
zero without the remote moving.
43+
- Symlinks and Windows junctions are refused instead of followed.
44+
- File and directory replacements are atomic, so an interrupted sync cannot
45+
leave a half-written config.
46+
- Nested `.git` directories left behind by skill installs are stripped from the
47+
staging copy, so skill contents are committed as files rather than as unusable
48+
gitlinks.
49+
- A cross-process lock prevents concurrent syncs from the plugin, the CLI and
50+
multiple OpenCode windows.

0 commit comments

Comments
 (0)