skill-mirror-sync
v1.2.0
Published
> 一个 opencode 插件,启动时将远端 skill 仓库完整镜像到本地,保持精确一致(含删除)。
Maintainers
Readme
skill-mirror-sync
一个 opencode 插件,启动时将远端 skill 仓库完整镜像到本地,保持精确一致(含删除)。
1. What it does
skill-mirror-sync is an opencode plugin that performs an exact-mirror sync of a remote skills endpoint into the local opencode skills cache directory at startup. It uses conditional HTTP GET requests (ETag / If-None-Match) to minimise bandwidth — unchanged files are not re-downloaded. The mirror is exact: skills removed from the remote index are deleted locally, extra local files are removed, and symlinks are replaced with regular files. The sync is startup-only and non-blocking (fire-and-forget); it never blocks opencode from booting.
2. Install
Add the plugin to your opencode.jsonc:
{
"plugins": [
{
"id": "skill-mirror-sync",
"options": {
"url": "https://implbureau.weschool.vip/.well-known/agent-skills/"
}
}
]
}IMPORTANT: You must also remove any skills.urls section from your opencode.jsonc. If skills.urls is present, opencode's built-in add-only downloader will also run alongside this plugin, causing conflicts. The plugin CANNOT remove skills.urls programmatically — you must delete it manually.
3. Configuration
The plugin accepts an options object with the following shape:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| url | string | "https://implbureau.weschool.vip/.well-known/agent-skills/" | Remote base URL for the skills index. Must be http or https. |
| path | string (optional) | — | Override the local skills directory. Must be an absolute path. |
| force | boolean | false | Allow destructive deletes on first sync even when no prior state exists. |
| staleTimeoutMs | number | 120000 | File lock staleness threshold (ms). Must be > requestTimeoutMs * 3 and > 30000. |
| downloadConcurrency | number | 4 | Max concurrent file downloads (1–16). |
| requestTimeoutMs | number | 30000 | Per-request timeout (ms). |
4. Server contract
WARNING: The server MUST regenerate or touch index.json after ANY skill file content change. This plugin uses Scheme B: per-file ETags are accumulated in local state from prior HTTP responses, not from the index. If index.json is not updated (or its ETag does not change), the plugin will see a 304 Not Modified response and will not re-check individual files — even if their content has changed.
The server should serve strong ETags for index.json and all skill files. Apache and nginx do this by default. Weak ETags (e.g. W/"abc") are also supported and stored as-is.
5. How sync works
- Startup-only: The sync runs once when opencode loads, asynchronously (fire-and-forget). No background timers or file watchers.
- Conditional GET: On each run, the plugin fetches
index.jsonwithIf-None-Matchusing the stored ETag. If the index is unchanged (304), only local drift (missing/extra files) is repaired. If the index changed (200), the full diff is computed and only changed/missing files are downloaded. - Exact mirror: Deletes are real — skills removed from the remote index are deleted locally. Extra local files are removed. Symlinks are unlinked and replaced with regular files.
- Multi-instance safety: A cross-platform file lock (mkdir-based) prevents two opencode instances from syncing concurrently. The second instance skips immediately (no waiting). Stale locks (e.g. from a crashed process) are reclaimed after
staleTimeoutMs. - Staged transaction: All downloads go to a staging directory first. Only after all downloads succeed are destructive operations (deletes, moves) applied. A mid-sync failure leaves prior state intact.
6. On-demand skill_sync tool
The plugin registers a skill_sync tool in opencode. You can invoke it at any time during a session to trigger a manual sync:
/skill_syncThe tool returns a structured summary: { status, downloaded, deleted, unchanged, errors }. This is useful if you want to pull in remote changes without restarting opencode.
7. Failure modes & troubleshooting
| Symptom | Likely cause | What to do |
|---------|-------------|------------|
| Skills not synced at startup | Network error (startup errors are swallowed) | Check stderr logs for [skill-mirror-sync] messages. Run /skill_sync manually. |
| "remote index is empty" warning | Remote index.json has zero skills | Verify the remote URL is correct. The plugin will NOT delete local skills in this case. |
| Mid-sync failure | Network timeout or server error | Prior state is preserved. The next sync (startup or manual) retries automatically. |
| Lock not acquired | Another opencode instance is syncing | The sync is skipped. Run /skill_sync after the other instance finishes. |
| "lock lost during sync" | Another instance reclaimed the lock | The sync aborts. Prior state is preserved. Run /skill_sync again. |
8. Limitations
- Startup-only: The sync runs only at opencode startup and on manual
/skill_syncinvocation. No periodic re-sync. - No rollback: Once applied, changes cannot be rolled back. Prior state is preserved for the next sync to converge from.
- No authentication: The remote server must be publicly accessible. No auth headers or credentials are supported.
- No content-corruption detection: Scheme B stores remote ETags, not local content hashes. A file that exists with wrong bytes but a matching ETag is not detectable.
- Platforms: macOS, Windows, and Linux are supported.
中文说明:
skill-mirror-sync是一个 opencode 插件,在启动时将远端 skill 仓库完整镜像到本地。它使用条件 GET 请求(ETag)只下载变化的部分,并保证本地与远端精确一致(包括删除远端已移除的 skill)。安装后需手动删除opencode.jsonc中的skills.urls配置。服务器运维人员需注意:修改 skill 文件内容后必须重新生成index.json,否则插件无法感知变化。
