WorkBuddy记忆跨设备同步(任意云盘)
在多台 Windows 机器之间,通过任意可在多机间自动同步文件的云盘(OneDrive / Dropbox / 百度网盘 / 坚果云 / 群晖 Drive 等)同步 WorkBuddy 的个人数据(配置、身份/记忆文件、项目记忆、自动化定义),并清理同步产生的冲突文件(.conflict)与"复活"文件(会话修改备份)。
何时触发
- 用户说"同步我的 WorkBuddy""跨设备同步 WB""push/pull 一下 WB"。
- 用户说"清理 WB 冲突文件""清理 .conflict""清理复活文件"。
- 用户在多机环境下想让配置/记忆/自动化保持一致。
何时不要触发
- 用户只用一台机器(无需同步)。
- 用户没用任何自动同步云盘(本技能依赖某个会自动同步文件的云盘作为中转)。
- 非 Windows / 无 PowerShell 环境。
前置条件
- Windows + PowerShell 5.1+。
- 已登录某个会自动同步文件的云盘(OneDrive / Dropbox / 百度网盘 等),且 WorkBuddy 数据在
%USERPROFILE%\.workbuddy。 - WorkBuddy 桌面端已安装。真实同步前必须完全关闭 WorkBuddy(任务管理器确认无 WorkBuddy/CodeBuddy 进程);否则数据库可能被锁、文件可能半写。
- Python 运行时(可选):用于同步"自动化定义"。缺失时自动跳过该段,不影响其余同步。
⚠️ 第一条铁律:先 dry-run,再 -Apply
所有脚本默认都是 dry-run(只读预览,不改任何文件)。 永远先跑 dry-run 看输出,确认没有意外变更后,再加 -Apply(或清理脚本加 -Execute)真正执行。这条纪律是为了防止误删/误覆盖。
脚本与用法
所有脚本在 scripts/ 目录。
1. 同步 wb-sync.ps1
# 预览(安全,不改文件)
powershell -ExecutionPolicy Bypass -File scripts/wb-sync.ps1 -Action push
powershell -ExecutionPolicy Bypass -File scripts/wb-sync.ps1 -Action pull
# 自检:检查路径/云盘中转/Python 是否就绪
powershell -ExecutionPolicy Bypass -File scripts/wb-sync.ps1 -Check
# 真正执行(先确认 WB 已关闭、dry-run 已看过)
powershell -ExecutionPolicy Bypass -File scripts/wb-sync.ps1 -Action push -Apply
powershell -ExecutionPolicy Bypass -File scripts/wb-sync.ps1 -Action pull -Apply
流程约定:离开 A 机前 push;到达 B 机后 pull。
关键参数(均可被 -Config <file.json> 覆盖,见 scripts/wb-sync.config.sample.json):
| 参数 | 默认 | 说明 |
|---|---|---|
-LocalWorkBuddy |
%USERPROFILE%\.workbuddy |
本机 WB 数据根 |
-WorkSpace |
D:\WB_WorkSpace |
真实工作树根 |
-Cloud |
%OneDrive%\WorkBuddy-Sync |
任意云盘的同步目录(默认 OneDrive;改用 Dropbox/百度网盘等只需传 -Cloud <路径>) |
-WorkCloudSub |
WB_WorkSpace |
云端工作树子目录名 |
-WorkExcludeTop |
杂项 |
整目录排除(数组) |
-ExcludeDirs |
多项机器本地目录 | 额外排除的机器本地目录 |
-PythonExe |
默认 WB 内置 Python | 自动化同步用的 Python |
-NoDelete |
关 | 关闭"删除传播" |
-Force |
关 | 绕过 push 护栏 / WB 运行检查(有风险) |
常用开关:-Apply(真正改文件)、-Check(自检)、-Force(强制)、-NoDelete(不传播删除)、-Config(载入 JSON 覆盖路径)。
2. 清理冲突 wb-cleanup-conflicts.ps1
逐文件比对 MD5:.conflict 内容与当前源一致 → 假冲突(可删);内容不同 → 真冲突(保留,需人工合并);源缺失 → 孤儿(保留待恢复)。
powershell -ExecutionPolicy Bypass -File scripts/wb-cleanup-conflicts.ps1 # 仅报告
powershell -ExecutionPolicy Bypass -File scripts/wb-cleanup-conflicts.ps1 -Execute # 删假冲突
3. 清理复活 wb-cleanup-resurrection.ps1
删除 WorkBuddy 会"复活"已删文件的源头:内部的 workspace 镜像目录,以及每个会话下的 modify_backup / .modify_backup_meta。通用实现,自动枚举所有会话,无硬编码 ID。
powershell -ExecutionPolicy Bypass -File scripts/wb-cleanup-resurrection.ps1 # 仅报告
powershell -ExecutionPolicy Bypass -File scripts/wb-cleanup-resurrection.ps1 -Execute # 删除(需 WB 已关)
4. 修复空自动化 ID fix-null-automation-id.py
当 3-way 合并曾漏写 id 列,导致新插入的自动化任务 id 为 NULL(列表中可见、但点不进去、永不调度、软删也够不到)时,用本脚本回填 id(格式 automation-<created_at_ms>)并修复存量记录。
python fix-null-automation-id.py --dry-run # 仅报告待修复项,不改库
python fix-null-automation-id.py # 写前自动备份 db,再修复
> 注意:本脚本直接操作 WorkBuddy 的 state.vscdb 自动化表;运行前请完全退出 WorkBuddy,脚本会先备份数据库再写入。
5. 查看跨设备同步审计轨迹 sync-history-report.ps1
把各机器分散在 ~/.workbuddy/sync-history/<本机名>.jsonl 的追加记录,合并 + 按时间排序,输出一份全局、零冲突的审计时间线(每台机器只写自己的文件,不存在共享文件被覆盖,故天生零冲突)。在任一台机器 pull 之后运行,即可看到全部机器的完整轨迹。
# 默认读取 %USERPROFILE%\.workbuddy\sync-history,输出全局时间线
powershell -ExecutionPolicy Bypass -File scripts/sync-history-report.ps1
# 只看最近 10 条
powershell -ExecutionPolicy Bypass -File scripts/sync-history-report.ps1 -Last 10
# 只看某台机器
powershell -ExecutionPolicy Bypass -File scripts/sync-history-report.ps1 -Machine GY-PTYF-YINGJIE
# 导出原始 JSON(便于程序消费)
powershell -ExecutionPolicy Bypass -File scripts/sync-history-report.ps1 -Json
# 同时写入文件
powershell -ExecutionPolicy Bypass -File scripts/sync-history-report.ps1 -OutFile sync-history-report.txt
输出含两截:① 时间线表(本地时间 / 动作 / 本机 / 来源机 / 传输数 / 冲突数 / 删除数);② 每台机器最近活动(总条数、最近 push、最近 pull)。copied/conflicts/deleted 为 null 的历史种子(手动补录的最近一次)以 - 显示。
安全机制(实现要点)
- Push 护栏:push 前比对"本机上次 push/pull 时间"与"云端上次 push 时间",本机落后则中止(先 pull,或
-Force)。防止用陈旧本地数据覆盖云端新内容。 - 首同步保护:无基线时,较旧源文件不会覆盖较新目标。
- MD5 内容比对:用内容哈希而非修改时间判变更,规避云盘改写 mtime 造成的伪冲突。
- 删除传播(墓碑):删除通过共享
wb-sync-state.json里的 tombstone 传播;目标若被独立修改则保留(删除 vs 修改冲突)。 - 空目录清理:删除传播后递归清理真正空的目录(1.1.8 修复:push 时 WorkSpace 段清理曾被「wsDst 在 dstRoot 内」守卫误跳过,导致云端 WorkSpace 空目录永不清理;现仅当两根完全相同才跳过)。
- 默认 dry-run:无
-Apply绝不改文件。 - 操作历史(Plan B):每次 push/pull 向
~/.workbuddy/sync-history/<本机名>.jsonl追加一行 JSON(含t时间 /machine机器 /action动作 /source来源机 /copied传输数 /conflicts冲突数 /deleted删除数)。该目录已列入白名单同步,故各机互相拉取后汇总为全局、零冲突的审计轨迹;读取所有*.jsonl按t排序即得完整跨设备时间线。 - 属主独占写(1.1.7):每台机只推自己的
<本机名>.jsonl。别人的审计文件只会被拉下来(只读镜像),绝不回推、也绝不为其传播删除。为什么必须这样:审计文件是追加式的,若允许非属主回推,一台持有陈旧副本的机器会因"本地与基线不一致"而把自己那份较短的副本覆盖上云,属主下次 pull 就把自己的轨迹回退了(追加式不再成立)。此改动同时修掉了一个已实测的sync-history\<他机名>.jsonl冲突。 - push 先比 hash 再比 mtime(1.1.9):push 自己的审计文件时整体绕开 hash 三向合并(即永不进入
.conflict生成路径),改用更克制的策略:① 云端缺该文件 → 直接新建;② 两端 hash 相同 → 内容一致,跳过不覆盖;③ hash 不同才比 mtime——本地更旧则跳过不覆盖(绝不把云端的更新副本往回滚),本地更新才覆盖,且覆盖也不产生.conflict。因为审计日志是追加式的、本机恒权威,这套"相同跳过 / 旧跳过 / 新覆盖且不冲突"的组合彻底消除了sync-history\*.jsonl.conflict的产生。 - 机器本地元数据永不跨机(1.1.3 起,1.1.4/1.1.6 扩充):凡"内容只对本机成立"的文件一律不进同步,且按其配对整组处理,绝不同步半套(同步半套会让两台机彼此对不上)。目前五组:
- MCP 组:
.mcp.json/mcp.json(服务定义,内含本机回环端口127.0.0.1:<port>与明文 Bearer 令牌,同步=把凭据写进云盘,故不可同步)+mcp-approvals.json(工具审批授权;若单独同步,会在没有对应服务定义的机器上凭空授权、或继承你从未批准过的免确认)。 - 每机运行态组:
user-state.json(启动/提示计数)+workspace-state.json(引导/首次运行时间戳)。 - 市场缓存组:
_skillhub_meta.json(内含本机绝对路径iconLocalPath)+.connectors-marketplace.meta.json(远程连接器包的 etag/sha;其载荷缓存目录connectors-marketplace\本就不同步,单独送来 etag 会让对端跳过自己的刷新)。 - 一次性迁移台账组(1.1.6 补齐):
skills\.model_invocation_to_override_migration.json、skills\.disable_to_model_invocation_migration.json、skills\.user_invocable_only_to_off_migration.json、skills\_bm_skillid_migration.json("本机已迁移"的记录;若随同步传过去,对端会跳过它自己的迁移,造成技能未被迁移;四者同属一类,必须成组排除)。 - 垃圾文件组(1.1.6):任意位置的
.DS_Store(macOS Finder 元数据,随技能包混入 Windows,纯垃圾)。 - automations 快照规范化(1.1.3):导出
automations.json时把运行时列next_run_at/last_run_at置空(import 本就忽略这两列),使两台机导出的快照字节稳定,消除每次 pull 必现的 automations 假冲突。 - automations id 冲突守卫(1.1.11):导入 cloud 的
automations.json时,若某任务的本地行缺少id、而 cloud 的id已被本地另一任务占用,旧逻辑会直接UPDATE automations SET id=...触发UNIQUE constraint failed: automations.id崩溃。1.1.11 在回填前预检占用:冲突则跳过回填并打印[WARN] cloud id already exists locally under a different task, skipping id backfill:,让导入继续;该任务仍可用(name,cwds)键管理。典型触发场景:跨机 rename/duplicate 后 id 分配不一致。 - 状态文件副本自清(1.1.3):每次落盘后自动清理云盘里 OneDrive 生成的
wb-sync-state-<设备名>*.json冲突副本(脚本只读/写正式名wb-sync-state.json,副本是 OneDrive 在两机并发重写时产生的、纯垃圾)。
始终排除(不可关闭)
以下项永远不同步,即使 -ExcludeDirs 也没法打开——这是安全底线:
connectors(含凭证,同步到云端会泄露凭据)modify_backup*/.modify_backup_meta(机器本地撤销快照,是"复活"源头)*.conflict(同步遗留,避免自喂养循环)
机器本地文件(按配对整组排除,绝不同步半套) —— 判据是"内容只对本机成立":
| 配对 | 文件 | 为什么不能跨机 |
|---|---|---|
| MCP 组 | .mcp.json、mcp.json |
MCP 服务定义含本机命令/路径 |
| MCP 组 | mcp-approvals.json |
审批授权绑定本机 MCP 定义;单独同步→对端凭空授权/免确认 |
| 每机运行态组 | user-state.json |
启动次数/提示计数,纯本机量 |
| 每机运行态组 | workspace-state.json |
引导、首次运行时间戳,属本机安装事件 |
| 市场缓存组 | _skillhub_meta.json |
内含本机绝对路径 iconLocalPath,跨机必冲突 |
| 市场缓存组 | .connectors-marketplace.meta.json |
远程连接器包 etag/sha;载荷目录 connectors-marketplace\ 不同步,单独送 etag 会让对端跳过刷新 |
| 一次性迁移台账 | skills\.model_invocation_to_override_migration.json |
"本机已迁移"记录;传过去会让对端跳过自己的迁移 |
| 一次性迁移台账 | skills\.disable_to_model_invocation_migration.json |
同上(migratedAt/scanned/skipped 记录,只对本机成立) |
| 一次性迁移台账 | skills\.user_invocable_only_to_off_migration.json |
同上({version:1} 标记,只对本机成立) |
| 一次性迁移台账 | skills\_bm_skillid_migration.json |
同上(migratedAt/version 标记,只对本机成立) |
| 垃圾文件 | 任意位置的 .DS_Store |
macOS Finder 元数据,随技能包混入 Windows;纯垃圾 |
| 凭证组 | app\connector-keys\*.key |
连接器凭证的解密密钥;其配对存储 connectors\ 已是锁定排除,密钥必须一并留在本机(同进同出) |
有意保留同步的相邻文件(与上表成对、但内容机器无关):_meta.json / workbuddy.json / meta.json(技能版本与展示名)、技能目录下的 _icon.*(图标实体,各机各自持有)、settings.json、models.json、ioa-im-override.json、app/app-config.json(含你的个性化 customPrompt)。
默认额外排除(可在 -ExcludeDirs 增减):artifact-index、automation-backups、projects、sessions、tasks、workspace(机器本地运行时数据,跨机必冲突、体积大)。有意保留同步:MEMORY.md、USER.md、automations.json(跨机一致性有用,冲突时人工合并)。
其中 tasks(对话级任务)的排除是技术结论而非取舍,见下一节。
技能同步范围(skills\)—— 整体不跨机同步
skills\ 下所有技能两个方向都跳过(不推送、不拉取)。机制层面已移除 SkillsAllow 白名单——Is-Excluded 直接整体排除 skills\ 子树。
判据:能否在对端复现。能复现的就不必搬——搬过去纯属浪费带宽、放大冲突面。逐项实证(本机 22 个技能):
| 技能 | 来源 | 对端复现方式 |
|---|---|---|
ppt-master |
GitHub hugohe3/ppt-master |
重新 clone |
wind-alice / wind-mcp-skill |
~/.agents\.skill-lock.json 记 sourceType: git → https://gitee.com/wind_info/wind-skills.git |
npx skills add <仓库>(且自带每日自更新) |
ima-skill |
审计日志:curl https://app-dl.ima.qq.com/skills/ima-skills-1.1.8.zip |
重新下载该 zip |
| 其余 17 个 | WorkBuddy 技能市场(带 _skillhub_meta.json) |
WB 自动下载安装 |
workbuddy-data-sync |
自建 | ❌ 不可复现 → 唯一必须同步者 |
实现说明:
- 用显式允许名单,不用"有没有
_skillhub_meta.json"动态判定。 市场技能集合是动态的,而该标记文件本身是机器本地(不同步),动态判定会在两台机上不对称——同一路径在两机结论可能不同,正是要避免的。 - 排除 ≠ 删除。
Sync-Deletions对排除路径直接continue,故新增排除不会在对端删文件;对端保留已有的技能实体,仅是不再跨机更新。只有新加入的机器才需要按上表自行复现那 4 个技能。 - 顺带收益:state 基线瘦身约 90%(
ppt-master一个就 ~12k 文件),wb-sync-state.json从 MB 级降到百 KB 级。死掉的基线条目在下次-Apply时自动剪除(见下节)。 - 启用/停用偏好仍同步:
settings.json里的skillOverrides不排除。它机器无关,且对端 WB 自动装上某个市场技能时会立即生效,恰好保住"关掉防臃肿"的意图;若不同步,对端自动装上的技能会全部默认开启。 - ⚠️ 由此消解的"半套":此前市场技能内容在同步、而其
_skillhub_meta.json自 1.1.5 起被排除,导致对端有技能实体却无市场台账(该技能在对端的市场更新检测失效)。整类排除后,对端市场技能完全由 WB 自装自管,半套问题不再存在。
状态瘦身:死基线的自动剪除
wb-sync-state.json 里 baselines 记录每个同步过的文件的 MD5。当某路径被排除(skills\ 允许名单、connectors、机器本地文件、默认 -ExcludeDirs 等)或属于他机审计文件后,它既不会被复制、也不会被传播删除,其基线永无再用之日——纯死重。脚本在每次真实同步落盘前自动剪除这类条目(含对应 tombstone),并打印剪除数量。
安全性:若某路径日后重新变得可同步,缺失的基线会让三方合并退化为首同步模式(保留较新者、把另一侧备份成 .conflict),不会静默覆盖;而剪掉一个 tombstone 只意味着"不删对端副本",正是排除语义。
对话级任务(tasks\)默认不同步 —— 不是取舍,是技术上不可用
对话里的任务清单存在 ~/.workbuddy\tasks\<对话ID>\N.json(subject / description / activeForm / status / createdAt)。它不在 automations.json(那里只有定时任务),且默认整类排除。
为什么不能同步(实测):
- 任务的宿主是对话,不是文件。 对话的身份与元数据存在
~/.workbuddy\workbuddy.db的sessions表(id/title/cwd/model/permission_mode…)。实测 23 个任务夹中有 20 个能在本机库查到同名对话行(例:0a3ee5f1-5236-4540-aefd-efb8415265c7→ "上传任务成果到资料库文档目录")。对话 ID 是本机生成的。 - 宿主库被锁死。
workbuddy.db/-shm/-wal是锁定排除($BlockedFiles),永不跨机:WB 运行时会锁库,脚本也无法安全地三方合并两台机的运行时库。对话正文则在projects\<cwd-slug>\<对话ID>.jsonl,同样默认排除(体积可达数百 MB)。 - 后果。 强行打开
tasks只会把任务搬成孤儿:对端的对话列表来自它自己的库,没有对应对话 → 任务在 UI 里看不到、点不进去、也永不清理,只会越积越多。要让它"有意义",就得同步整个workbuddy.db—— 而那正是本工具设计上锁死不做的事。结论:技术上做不到"有意义地同步",故保持默认排除。
> 顺带澄清一个易混点:sessions\*.json 不是对话,是主机运行态(pid / sessionId: interactive-<pid> / 127.0.0.1:<port> / hostname / cwd),本身就只对本机成立,不应同步。
故障排查
- 出现
.conflict:要么是真冲突(两端内容确实不同,需人工合并保留哪边),要么是旧假冲突(用wb-cleanup-conflicts.ps1 -Execute清)。connectors凭证冲突绝不可跨机合并,保留本机。 - 技能目录下
_skillhub_meta.json.conflict:1.1.3 起该文件已不再同步,不会再产生;历史残留可直接删。 automations.json.conflict:1.1.3 起导出已置空运行时列next_run_at/last_run_at,不再产生;历史残留可直接删。- 云盘里出现
wb-sync-state-<设备名>*.json:是 OneDrive 自己的版本冲突副本(非脚本产物);1.1.3 起脚本每次运行会自动清理,也可手动删(务必保留正式名wb-sync-state.json)。 mcp-approvals.json/workspace-state.json/.connectors-marketplace.meta.json/.model_invocation_to_override_migration.json的.conflict:1.1.4 起这些文件已列入机器本地、不再同步,不会再产生;历史残留可直接删。app\connector-keys\*.key:32 字节二进制密钥,是连接器凭证的解密密钥。1.1.5 起已列入机器本地、不再上云(与锁定排除的connectors\同进同出)。若云盘里还留有旧副本,属历史残留,可删。.mcp.json(带点,旧名):本地已无此文件,但云盘可能留有历史副本(早期白名单遗留)。它只是惰性残留(现在既不推也不拉),可删。当前真实文件是无点的mcp.json,内含本机回环端口与明文 Bearer 令牌,故不可同步(同步会把凭据写到云盘)。app\app-config.json:含defaultWorkspacePath(目前两机均为D:\WB_WorkSpace,故一致);该文件因携带你的个性化customPrompt而有意保留同步。仅当某台机的默认工作区路径不同时才会冲突。skills\下 4 个迁移台账与.DS_Store的.conflict:1.1.6 起.disable_to_model_invocation_override_migration.json/.user_invocable_only_to_off_migration.json/_bm_skillid_migration.json/.model_invocation_to_override_migration.json与任意位置的.DS_Store均已列入机器本地/垃圾文件,不再同步,不会再产生;历史残留可直接删。tasks\<对话ID>\*.json在两台机不一样:设计如此,不是故障。任务的宿主对话只存在于各自机器的workbuddy.db(锁定排除),任务本身因此从不跨机。详见「对话级任务默认不同步」一节。skills\下任意技能的.conflict:skills\整体排除后不再产生任何同步冲突;历史残留可直接删。对端已有的技能实体不会被删除(排除 ≠ 删除),只是不再跨机更新。skills\ppt-master等技能在对端没有 / 版本不一致:设计如此。这些技能可复现(GitHub / gitee-npx / 市场自动装),按「技能同步范围」一节的表在对端自行复现即可。若对端本来就有,则保留原样、只是不再更新。sync-history\*.jsonl冲突:1.1.7 起审计日志设为属主独占写(非属主只拉不推、不传播删除);1.1.9 起属主 push 自己的文件整体绕开 hash 三向合并(hash 相同跳过 / 本地更旧跳过 / 本地更新才覆盖);1.1.10 起 pull 也不再走三向合并(此前因状态瘦身会剪掉他机日志的 baseline,pull 每次都落进"无基线 → FIRST-SYNC DIFF"从而误生成.conflict):本机日志仅在本地缺失/为空时才从云端播种、否则一律跳过;他机日志则 hash 相同跳过、本地更新跳过、云端更新(或 mtime 相同)才覆盖,两个方向都不再产生.conflict。历史残留可删——但保留行数更多的那一份(属主本机那份通常最全,可从属主机重新 push 覆盖云端)。- 已删文件又出现:用
wb-cleanup-resurrection.ps1 -Execute清掉复活源头,再push。 - push 被护栏拦:说明本机比云端旧,先
pull,或确认无误后-Force。 - WB 内部目录布局变了(WB 升级后):先跑
-Check自检;本技能依赖 WB 内部.workbuddy布局(非稳定公开 API),升级后可能静默失效,务必先看 dry-run。 - 脚本报中文乱码 /
意外的标记/字符串缺少终止符:.ps1里只要有中文,文件必须带 UTF-8 BOM,否则 Windows PowerShell 5.1 会按 GBK 解码 → 中文全乱、整脚本解析失败(sync-history-report.ps1曾踩此坑:无 BOM + 198 个非 ASCII 字节)。编辑脚本后请确认文件首字节是EF BB BF;.py/.json不要加 BOM。
责任与免责声明
- 本技能会删除文件并将个人数据写入云盘。虽有多重安全机制,但作者不保证零数据丢失。
- 使用者需自行确认 dry-run 输出、自行承担执行
-Apply/-Execute的后果。 - 凭证(
connectors)已被强制排除,但使用者仍应确认云端不含敏感文件。 - 本技能按 MIT 协议开源,按"原样"提供,不含任何明示或暗示担保。
发布/安装说明
- 安装:将整个
WorkBuddy记忆跨设备同步目录放入~/.workbuddy/skills/(用户级)或<项目>/.workbuddy/skills/(项目级)。 - 本技能可经
workbuddy skills scan安全扫描后,由开发者在技能市场发布。