1. 项目缘起:二十个 skill 散在三台电脑,到底有多痛
先说清楚这个项目到底在解决什么问题。我手头有三台常用设备:一台主力台式机放在家里,一台笔记本随身带着跑外勤,还有一台放在办公室的备用机。三台机器上各自装了一堆 AI 工具链相关的 skill——有的负责代码审查,有的负责文档摘要,有的负责把会议录音转成结构化笔记,还有几个是专门给特定项目定制的提示词封装。零零散散加起来,正好二十个出头。
问题出在“散”这个字上。每个 skill 本质上就是一组配置文件、脚本、提示词模板和依赖清单的组合,它们分散在不同机器的不同目录里,版本还不完全一致。我在台式机上调试好的一个 skill,到了笔记本上可能因为 Python 版本差了一个小版本就跑不起来;办公室那台机器上的某个 skill 更是三个月没同步过,里面的 API 调用方式早就过时了。每次换设备工作,光是回忆“这个 skill 在哪台机器上、是什么版本、依赖装没装”就要花掉十几分钟,真正干活的时间反而被压缩了。
更麻烦的是,这二十个 skill 之间还有依赖关系。比如“代码审查”这个 skill 依赖一个共享的提示词库,“文档摘要”又依赖同一个库里的另一部分模板。如果我只同步单个 skill 而忘了同步共享库,运行起来就会报错,报错信息还特别隐晦,往往要翻好几层日志才能定位到是共享库版本不对。这种问题在单台机器上不容易暴露,一旦跨设备就频繁出现。
所以这个项目的核心目标很明确:让 AI 自己把这二十个 skill 从一台机器“搬”到另外两台机器上,并且保证搬完之后能直接跑,不需要我手动去每台机器上逐个检查依赖、改路径、调版本。一句话概括就是——我说一句“把这二十个 skill 同步到另外两台机器”,AI 自己完成打包、传输、解包、依赖安装、路径修正、版本校验的全过程。
这个需求听起来简单,但真正落地时会碰到几个硬骨头。第一,三台机器的操作系统不完全一样,一台是 Linux,一台是 macOS,还有一台是 Windows 下的 WSL 环境,路径分隔符、换行符、可执行权限这些细节都不同。第二,skill 的依赖有 Python 包、Node 包、系统级命令行工具,还有几个是纯配置文件不需要安装,AI 需要能区分对待。第三,传输过程要保证完整性,不能传了一半断了导致目标机器上出现“半成品”skill,那种状态比没有还糟糕。
适合参考这个方案的人,我觉得是这几类:手头有多台设备、经常需要在不同机器上切换工作环境的开发者;正在搭建自己的 AI Agent 工具链、skill 数量超过十个、开始感到管理吃力的人;以及任何对“让 AI 自己完成环境配置”这件事感兴趣、想看看实际落地边界在哪里的同行。如果你只有一台机器、skill 不超过五个,那手动管理完全够用,没必要上这套方案。但一旦超过十个、跨两台以上设备,这套思路的收益就会非常明显。
2. 整体设计思路:为什么选择“清单驱动 + 分阶段执行”
2.1 核心思路拆解
整个方案的核心思路可以概括成一句话:用一份机器可读的清单描述所有 skill 的状态,AI 读取清单后分阶段执行同步,每个阶段都有校验和回滚机制。这句话里有三个关键点,我逐个拆开说。
第一个关键点是“机器可读的清单”。我没有让 AI 去扫描目录来推断有哪些 skill,因为扫描出来的结果不可靠——有些目录是临时文件,有些是废弃的旧版本,AI 分不清哪些该同步哪些不该同步。所以我维护了一份 YAML 格式的清单文件,里面明确列出每个 skill 的名称、路径、类型、依赖列表、目标机器标识。这份清单是唯一的事实来源,AI 只认清单,不认目录扫描结果。这样做的好处是可控性极强,我想同步哪个就写哪个,不想同步的即使存在于目录里也不会被带上。
第二个关键点是“分阶段执行”。同步不是一步到位的事情,我把它拆成了五个阶段:清单校验、打包、传输、目标端解包与安装、最终验证。每个阶段结束都有明确的成功/失败判定,失败就停下来报告原因,不会带着问题继续往下走。这种设计借鉴了持续集成流水线的思路——宁可早失败、快失败,也不要等到最后才发现某个环节出了问题。
第三个关键点是“校验和回滚”。打包完成后计算校验和,传输完成后在目标端重新计算并比对,不一致就重新传。安装阶段如果某个 skill 的依赖装不上,AI 会记录失败项并继续处理其他 skill,最后汇总报告哪些成功哪些失败,而不是遇到第一个错误就整个中断。对于已经部分安装的 skill,提供一个回滚脚本可以清理掉本次安装产生的文件,恢复到同步前的状态。
2.2 方案选型背后的考量
为什么不用现成的配置管理工具,比如 Ansible 或者 Chef?我实际评估过,结论是对于“二十个 skill、三台机器”这个规模,引入 Ansible 的复杂度收益比不划算。Ansible 需要目标机器有 SSH 服务、需要配置 inventory、需要写 playbook,学习成本和维护成本都不低。而且我的 skill 里有相当一部分是纯配置文件,用 Ansible 的 file 模块去拷贝反而绕远了。更重要的是,我希望 AI 能理解整个流程并自主决策,而不是我写死一个 playbook 让 AI 去执行——那样 AI 就退化成了一个命令执行器,失去了“自己判断该怎么做”的价值。
那为什么不用 Git 仓库来同步?Git 确实能解决版本管理和传输问题,但解决不了依赖安装和环境适配。我的 skill 依赖里有系统级的命令行工具,有需要编译的 Python 包,这些都不是 Git 能处理的。而且 Git 仓库要求所有机器都能访问同一个远程仓库,我的三台机器网络环境不同,有的能直连有的需要走内部镜像,统一远程仓库反而增加了不确定性。
最终选择的方案是“清单 + 打包 + 分阶段脚本 + AI 调度”。AI 的角色不是执行具体命令,而是理解清单、判断每个 skill 的类型、决定用哪种方式处理、在遇到问题时选择合理的应对策略。比如遇到一个纯配置文件的 skill,AI 直接拷贝即可;遇到一个有 Python 依赖的 skill,AI 需要先检查目标机器的 Python 版本,再决定是直接 pip install 还是先创建虚拟环境。这种判断能力是现成工具给不了的。
2.3 三台机器的环境差异与应对策略
三台机器的环境差异是这套方案必须正面处理的问题。我整理了一张对照表,把关键差异和应对方式列清楚:
| 差异项 | 台式机(Linux) | 笔记本(macOS) | 备用机(WSL) | 应对策略 |
|---|---|---|---|---|
| 路径分隔符 | / | / | /(WSL 内) | 统一用/,WSL 内不跨到 Windows 路径 |
| 换行符 | LF | LF | LF | 打包时统一转 LF,避免 CRLF 混入 |
| Python 版本 | 3.11 | 3.9 | 3.10 | 清单中标注最低版本要求,AI 检查后决定是否创建虚拟环境 |
| Node 版本 | 20.x | 18.x | 20.x | 同上,标注最低版本 |
| 可执行权限 | 需要 chmod | 需要 chmod | 需要 chmod | 打包时保留权限位,解包后校验 |
| 系统命令行工具 | 齐全 | 部分缺失 | 部分缺失 | 清单中标注系统依赖,AI 检查后提示手动安装 |
这张表是我踩过坑之后总结出来的。最开始我没在意换行符的问题,结果在 WSL 里跑一个从 macOS 传过来的 shell 脚本时一直报“bad interpreter”错误,排查了半天才发现是 CRLF 在作怪。从那以后,打包阶段就强制把所有文本文件的换行符统一成 LF,这个操作成本极低但能避免一大类问题。
Python 版本差异也是个大坑。我有个 skill 用到了 Python 3.10 才引入的 match 语句,在笔记本的 3.9 上直接语法报错。后来我在清单里给每个 skill 标注了min_python字段,AI 在安装前会检查目标机器的 Python 版本,不满足就自动创建虚拟环境并安装指定版本的解释器。这个逻辑写起来不复杂,但省去了大量手动排查的时间。
3. 核心细节解析:清单结构、打包策略与 AI 调度逻辑
3.1 清单文件的结构设计
清单文件是整个方案的基础,它的结构设计直接决定了 AI 能不能准确理解每个 skill 的状态。我用的是 YAML 格式,因为 YAML 对人类友好、对机器也友好,AI 解析起来不容易出错。一个典型的 skill 条目长这样:
skills: - name: code-review path: ~/ai-skills/code-review type: python min_python: "3.10" dependencies: - openai>=1.0.0 - pyyaml>=6.0 system_deps: - git targets: - desktop - laptop - backup shared_libs: - ~/ai-skills/shared/prompts逐字段解释一下。name是 skill 的唯一标识,AI 在报告里引用它。path是源机器上的绝对路径,用~表示用户主目录,AI 在打包时会展开成实际路径。type标明 skill 的类型,目前支持python、node、config、script四种,不同类型走不同的安装流程。min_python和min_node是版本约束,AI 检查目标机器是否满足。dependencies是包管理器层面的依赖,system_deps是系统级命令行工具的依赖。targets列出这个 skill 需要同步到哪些机器,不是所有 skill 都需要同步到所有机器——有些 skill 只在台式机上用,就没必要传到笔记本上。shared_libs是共享库路径,AI 会确保这些共享库也被同步。
这份清单我是手动维护的,新增 skill 时手动加一条,删除 skill 时手动删一条。有人可能会问为什么不自动生成,我的考虑是:自动生成虽然省事,但容易把不该同步的东西也带进来,而且自动生成的清单缺少targets和shared_libs这类需要人工判断的字段。手动维护二十个条目,每次改动花不了一分钟,换来的是完全可控的同步范围,这笔账很划算。
3.2 打包策略:为什么用 tar.gz 而不是 zip
打包格式我选了 tar.gz,没有选 zip。原因有三个。第一,tar.gz 能保留文件权限位,zip 在跨平台时对权限的处理不一致,有时候会把可执行脚本的权限丢掉。第二,tar.gz 对符号链接的支持更好,我的 skill 目录里有几个符号链接指向共享库,zip 处理符号链接的方式在不同工具里不一样,容易出问题。第三,tar.gz 的压缩率通常比 zip 高一些,虽然差别不大,但传输时间能省一点是一点。
打包命令本身很简单:
tar -czf skills-bundle.tar.gz \ --exclude='__pycache__' \ --exclude='.git' \ --exclude='node_modules' \ -C ~ ai-skills/code-review ai-skills/doc-summary ...这里有几个细节值得说。--exclude排除了三类目录:__pycache__是 Python 编译缓存,没必要传;.git是版本历史,传过去只会增加体积;node_modules是 Node 依赖,目标机器上会重新安装,传过去纯属浪费带宽。-C ~是先切换到主目录再打包,这样包内的路径就是相对路径,解包时不会因为绝对路径差异出问题。
打包完成后计算 SHA256 校验和:
sha256sum skills-bundle.tar.gz > skills-bundle.sha256校验和文件跟着压缩包一起传输,目标端解包前先校验,不一致就重新传。这个步骤看起来多余,但我实际遇到过传输过程中网络抖动导致文件损坏的情况,有了校验和就能第一时间发现,而不是等到解包时报一堆莫名其妙的错误。
3.3 AI 调度逻辑:怎么让 AI 自己判断该做什么
这是整个方案里最核心也最难的部分。AI 不是简单地执行我写好的脚本,而是要根据清单和当前环境自主判断每一步该怎么做。我通过提示词工程把判断逻辑注入给 AI,核心提示词大致是这样的结构:
你是一个 skill 同步助手。你面前有一份清单文件,列出了需要同步的 skill。你的任务是:第一步,读取清单并校验每个 skill 的源路径是否存在;第二步,检查目标机器的环境是否满足每个 skill 的版本要求;第三步,对满足条件的 skill 执行打包;第四步,将打包文件传输到目标机器;第五步,在目标机器上解包并安装依赖;第六步,验证安装结果并生成报告。每一步完成后输出状态,遇到无法自动处理的问题时停下来询问我。
这段提示词的关键在于“遇到无法自动处理的问题时停下来询问我”。我不希望 AI 自作主张地处理所有情况,因为有些情况它判断不了。比如某个系统级依赖在目标机器上缺失,AI 可以检测到缺失,但要不要安装、怎么安装,这涉及到系统权限和包管理器选择,应该由我来决定。AI 的职责是发现并报告问题,而不是替我做所有决定。
实际运行下来,AI 在以下几个判断点上表现得比较可靠:判断 skill 类型并选择对应的安装流程、检查 Python/Node 版本是否满足要求、比对校验和、汇总成功失败列表。在以下几个判断点上还需要我介入:系统级依赖的安装方式选择、共享库版本冲突的解决、目标机器磁盘空间不足时的清理策略。这个分工是合理的,AI 做它擅长的重复性判断,我做需要上下文知识的决策。
4. 实操过程:从一句话指令到三台机器全部就绪
4.1 源机器上的准备与打包
实操的第一步是在源机器上确认清单文件是最新的。我打开清单文件,扫一眼最近有没有新增或删除的 skill,有的话手动更新清单。这一步花不了一分钟,但能避免“同步完了才发现漏了一个”的尴尬。
确认清单后,我把打包指令发给 AI。指令就是一句话:“按照清单把需要同步的 skill 打包,排除缓存和依赖目录,生成校验和。”AI 收到指令后,先读取清单,逐个检查源路径是否存在。如果某个路径不存在,它会报告出来并跳过该 skill,继续处理下一个。这个行为是我特意设计的——一个 skill 路径写错了不应该阻塞整个流程,跳过并报告比直接中断更合理。
检查通过后,AI 执行打包命令。打包过程中它会实时输出进度,哪个 skill 正在被打包、当前包体积多大。二十个 skill 打包下来大概需要十几秒,主要时间花在压缩上。打包完成后生成校验和文件,AI 把压缩包路径和校验和一起报告给我。
这里有个实操心得:打包前先清理一遍__pycache__和临时文件。我最初没做这一步,结果包体积比预期大了将近一倍,传输时间也翻倍。后来在打包命令里加了--exclude,包体积立刻降下来了。如果你用的是 Python skill,还可以在打包前跑一遍find . -name '__pycache__' -type d -exec rm -rf {} +彻底清理,效果更彻底。
4.2 传输环节的选型与配置
传输环节我试过三种方式,最后选了最朴素的一种:通过共享目录中转。三台机器都能访问一个网络共享目录,源机器把压缩包写进去,目标机器从里面读出来。这种方式的好处是不依赖 SSH 服务、不需要配置密钥、不受防火墙策略影响。缺点是速度取决于共享目录的带宽,但我的压缩包只有几十兆,传输时间可以接受。
具体操作是:源机器打包完成后,AI 把压缩包和校验和文件拷贝到共享目录的指定位置。然后在目标机器上,AI 从共享目录读取压缩包,先校验 SHA256,校验通过后解包。如果校验不通过,AI 会报告校验失败并建议重新传输,不会继续解包。
注意:共享目录的路径在三台机器上可能不同。我在清单里加了一个
shared_dir字段,每台机器配置自己的共享目录路径。AI 读取当前机器的配置,用对应的路径去读写。这个细节看起来小,但如果不处理,AI 就会用硬编码的路径去操作,在另一台机器上必然失败。
传输过程中还有一个坑:文件锁。有一次我在源机器上打包的同时,目标机器正在从共享目录读取上一次的压缩包,结果读到了一个不完整的文件。后来我加了一个简单的锁机制——打包时在共享目录创建一个.lock文件,传输完成后删除。目标机器读取前先检查.lock是否存在,存在就等待。这个机制很粗糙,但对于三台机器的场景足够用了。
4.3 目标机器上的解包与依赖安装
目标机器上的操作是整个流程里最复杂的部分,因为要处理环境差异。AI 拿到压缩包并校验通过后,按以下顺序执行:
第一步,解包到临时目录。不直接解包到最终位置,而是先解到/tmp/skills-sync/下面,确认无误后再移动到目标位置。这样做的好处是如果解包过程中出问题,不会污染现有的 skill 目录。
第二步,逐个 skill 检查依赖。对于python类型的 skill,AI 读取requirements.txt或清单里的dependencies字段,检查目标机器的 Python 版本是否满足min_python要求。不满足就创建虚拟环境,用指定版本的 Python 解释器。满足就直接在当前环境安装依赖。对于node类型的 skill,类似地检查 Node 版本和package.json。对于config类型的 skill,跳过依赖安装,直接拷贝配置文件。对于script类型的 skill,检查系统级依赖是否存在,缺失就报告。
第三步,处理共享库。AI 检查清单里的shared_libs字段,确保这些共享库也被同步到了目标机器。如果共享库已经存在且版本一致,跳过;如果不存在或版本不一致,从压缩包里解出来覆盖。
第四步,移动到最终位置。所有依赖安装完成后,AI 把临时目录里的 skill 移动到清单指定的目标路径。移动前会检查目标路径是否已存在同名 skill,存在的话先备份到.backup目录,再覆盖。这个备份机制救过我一次——有个 skill 的新版本有 bug,我靠备份快速回滚到了旧版本。
4.4 验证与报告生成
所有 skill 处理完成后,AI 生成一份同步报告。报告内容包括:成功同步的 skill 列表、失败的 skill 列表及失败原因、跳过的 skill 列表及跳过原因、每个 skill 的依赖安装结果、共享库的同步状态。报告以 Markdown 格式输出,我直接保存到笔记里备查。
验证环节我设计了两个层次的检查。第一层是文件级检查:确认每个 skill 的目标路径存在、关键文件存在、文件权限正确。第二层是功能级检查:对每个 skill 跑一个简单的冒烟测试,比如python -c "import skill_module"确认能导入,或者node -e "require('./skill')"确认能加载。冒烟测试不验证功能是否正确,只验证最基本的可运行性。这一层检查能发现大部分环境问题,比如依赖没装全、路径配错了。
报告生成后,AI 会把报告发给我确认。我扫一眼失败列表,如果有失败项,根据失败原因决定是手动修复还是调整清单后重新同步。大部分失败原因是系统级依赖缺失,这种我手动装一下就好。少数失败原因是版本冲突,这种需要我调整清单里的版本约束。
5. 常见问题与排查技巧实录
5.1 依赖安装失败的典型场景与处理
依赖安装失败是最高频的问题,我整理了一张速查表,覆盖了我实际遇到过的几种情况:
| 失败现象 | 可能原因 | 排查方法 | 处理方式 |
|---|---|---|---|
| pip install 报编译错误 | 缺少系统级编译工具 | 检查 gcc、make 是否存在 | 安装 build-essential 或对应工具链 |
| pip install 报版本冲突 | 已有包版本不兼容 | pip check查看冲突详情 | 创建独立虚拟环境隔离 |
| npm install 卡住不动 | 网络源不可达 | npm config get registry | 切换到可达的镜像源 |
| 脚本报 bad interpreter | 换行符是 CRLF | file script.sh查看 | 用 dos2unix 转换或重新打包 |
| 导入模块报 ModuleNotFound | 依赖没装或路径不对 | pip list和sys.path检查 | 补装依赖或修正 PYTHONPATH |
| 权限拒绝 | 可执行位丢失 | ls -l查看权限 | chmod +x 补上可执行位 |
这张表里的每一行都是我实际踩过的坑。比如“bad interpreter”那个,我一开始以为是脚本内容有问题,逐行检查了半天,最后用file命令一看才发现是 CRLF。从那以后,打包阶段强制转 LF,这个问题就再也没出现过。
还有一个坑是虚拟环境的路径问题。我在目标机器上创建了虚拟环境,但 skill 里的脚本用的是硬编码的 Python 路径,指向系统 Python 而不是虚拟环境里的 Python。结果依赖装在了虚拟环境里,脚本却用系统 Python 跑,自然找不到依赖。解决办法是在清单里加一个python_path字段,AI 根据这个字段生成正确的 shebang 行,或者在脚本里用#!/usr/bin/env python让系统自动找当前环境的 Python。
5.2 跨平台路径与权限的避坑指南
跨平台路径问题我总结了三条原则。第一条,清单里所有路径用~开头,AI 在运行时展开成实际的主目录路径。这样同一份清单在三台机器上都能用,不需要为每台机器单独维护一份。第二条,打包时用相对路径,通过-C参数切换工作目录,包内路径就是相对的,解包时不会因为绝对路径差异出问题。第三条,路径拼接用代码而不是字符串,AI 在生成路径时用os.path.join或pathlib.Path,避免手动拼/导致 Windows 下出问题。
权限问题主要出在可执行脚本上。tar.gz 能保留权限位,但前提是打包时权限就是对的。我有一次在源机器上忘了给一个脚本加可执行权限,打包传过去之后目标机器上也没有可执行权限,运行时报“Permission denied”。后来我在打包前加了一步检查:遍历所有.sh和.py文件,确认有可执行权限,没有就自动补上。这个检查逻辑很简单,但能避免一大类问题。
提示:WSL 环境下有一个特殊问题——WSL 内的文件权限和 Windows 侧的权限是两套体系。如果你在 WSL 里操作 Windows 盘符下的文件,权限位可能不生效。我的做法是把所有 skill 都放在 WSL 的原生文件系统里(
~/下面),不跨到/mnt/c/下面去,这样就避开了权限体系冲突的问题。
5.3 同步中断与回滚的实操经验
同步过程中断是难免的,网络抖动、磁盘满、依赖装不上都可能导致中断。我的处理原则是:每个阶段都有明确的完成标记,中断后从最后一个完成标记继续,而不是从头再来。
具体实现方式是在共享目录里维护一个状态文件,记录当前进行到哪个阶段、哪些 skill 已经处理完成。AI 每次开始同步前先读状态文件,如果发现有未完成的同步任务,询问我是继续还是重新开始。继续的话从断点处接着做,重新开始的话先清理掉上次的残留文件再从头来。
回滚机制我设计得比较简单:在覆盖目标路径之前,先把原有内容备份到.backup目录。如果同步完成后发现有问题,手动把.backup目录恢复回去即可。我没有做自动回滚,因为自动回滚的判断条件很难写准确——什么算“有问题”?依赖装上了但功能不对算不算?这种判断还是交给人来做更靠谱。
有一次我同步了十个 skill,其中三个的依赖装失败了。AI 报告了失败列表,我手动把那三个的依赖补装好,其他七个已经正常工作了。这种“部分成功”的状态在实际操作中很常见,关键是要有清晰的报告让我知道哪些成功了哪些失败了,而不是笼统地说“同步完成”或“同步失败”。
5.4 AI 判断失误的边界与人工兜底
AI 在这套流程里不是万能的,我实际运行下来发现它在以下几个场景容易判断失误,需要人工兜底。
第一个场景是依赖版本冲突的解决。AI 能检测到冲突,但选择哪个版本、降级还是升级,它判断不好。我的做法是让 AI 报告冲突详情,我来决定版本约束,然后更新清单后重新同步。
第二个场景是系统级依赖的安装。AI 能检测到某个命令行工具缺失,但要不要装、用哪个包管理器装,它不应该替我做决定。我的做法是让 AI 报告缺失列表,我手动安装,装完后告诉 AI 继续。
第三个场景是共享库的版本兼容性。两个 skill 依赖同一个共享库的不同版本时,AI 不知道该怎么处理。我的做法是在清单里明确指定共享库的版本,AI 只负责同步指定版本,版本选择由我提前决定好。
这三个场景的共同点是:需要上下文知识或价值判断,而不是纯粹的逻辑推理。AI 擅长的是“检查、比对、执行、报告”这类确定性任务,不擅长“选择、权衡、决策”这类需要背景知识的任务。把这两类任务分开,AI 做前者,我做后者,整体效率最高。
6. 后续扩展与个人体会
这套方案目前稳定运行了几个月,二十个 skill 在三台机器之间的同步从最初的手动半小时缩短到了现在的几分钟。我后来又加了两个扩展:一个是定时自动同步,每天凌晨 AI 自动检查清单是否有更新,有的话自动执行同步,第二天早上三台机器都是最新状态;另一个是同步日志归档,每次同步的报告自动保存到指定目录,按日期命名,方便回溯“某个 skill 是什么时候同步到哪台机器的”。
如果 skill 数量继续增长,比如超过五十个,我可能会考虑引入更结构化的依赖管理方式,比如给每个 skill 加一个depends_on字段描述 skill 之间的依赖关系,AI 在同步时按拓扑排序决定安装顺序。但目前二十个的规模,手动维护清单完全够用,没必要过度设计。
我个人在实际操作中的体会是:让 AI 做它擅长的事,不要指望它做所有事。这套方案里 AI 承担了检查、打包、传输、安装、报告这些重复性工作,我承担了清单维护、版本决策、异常处理这些需要判断的工作。分工明确之后,整体效率提升很明显,而且出问题时我知道该找谁——AI 报告里没提到的就是我的责任,报告里提到的就是 AI 的责任,边界清晰。
最后分享一个小技巧:清单文件本身也纳入同步范围。我把清单文件放在共享目录里,三台机器都读同一份清单。这样我在任何一台机器上更新了清单,其他机器下次同步时自动读取最新版本,不需要手动拷贝清单文件。这个改动很小,但省去了“清单版本不一致”这个隐蔽的坑。