关于 office-repair
office-repair 是用于维护 Excel 工作簿或同步已被手动编辑的 Office 生成文件的技能,面向需要保留现有文件特性的团队。它通过扫描、逐项确认和写后核验来修复工作簿,或将手动编辑文件设为规范版本并停用旧生成器。
主要功能
- 01只读扫描工作簿结构与错误
- 02按单元格应用带旧值校验的修改
- 03备份并验证修复后的文件
- 04比较生成文件与手动编辑版本
- 05归档生成器并保护规范文件
适用场景
- 01修复带有切片器或保护设置的工作簿
- 02查找缓存错误和数据验证范围问题
- 03将手动编辑的 Office 文件设为规范版本
- 04防止旧生成器覆盖规范文件
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ gasserane/ personal-skills --skill office-repair安装后在 Claude Code 中输入/office-repair确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
evals/evals.jsonreferences/officeops-excel-api.mdscripts/office_repair.pytest_office_repair.py详细介绍
/office-repair — 就地修复工作簿,或将生成的文件交给 Ane
只有一项任务:Ane 已经拥有的 Office 文件需要处理,而且以磁盘上的版本为准。
所有 Office 操作都在 ane_package.officeops 中完成。此技能提供判断,而非底层操作。如果某种模式需要 officeops 尚不具备的功能,就将其添加到 officeops 并在那里测试——office-review-pass 就是这条规则的实践范例;Wave 3 也以这种方式新增了五项功能,而没有另起炉灶。references/officeops-excel-api.md 中有准确的调用签名;编写任何代码前请先阅读。
原则如下,以免反复争论:任何会打开 Office 文件的操作都属于 officeops。 对两个字符串列表进行比对、发现自动更正造成的改动并重写 .py 生成器,都不属于 Office 操作,因此这些操作应放在 scripts/office_repair.py 中,并在那里进行测试。
模式路由
- repair — Ane 维护的 Excel 工作簿出错或需要修改,且包含简单写入会破坏的功能。
- canonical — 生成的制品经过了手动编辑,因此生成器现在成了过时副本。
如果修复后的工作簿背后还有生成器,这两种模式就会衔接起来。先运行 repair,然后运行 canonical 来弃用生成器。
两种模式的共同规则
- Ane 的文件是基准。 目标文件存在时,遵循 mel_wiki/wiki/concepts/edit-preservation-protocol.md。读取磁盘上的内容,只修改指定部分,其他内容保持逐字节不变。之所以需要此技能,是因为对于二进制文件而言,最难遵守这条规则;一旦丢失内容,既不会被察觉,也无法恢复。
- 如果 Office 文件包含 openpyxl 无法识别的功能,绝不能用 openpyxl 写入。 openpyxl 完全无法表示切片器:加载并保存后,
xl/slicers/会直接消失,工作簿仍可打开,也不会有任何提示。因此扫描是只读的,这不仅仅是出于安全考虑。 - 对写入后的文件进行验证,并与写入前的状态比对。 先运行
workbook_inventory,之后运行assert_inventory_unchanged。保存成功并不能证明任何事。 - 绝不编造事实。 单元格地址、工作表名称、数字、公式都不能编造。如果扫描结果或已加载的上下文中没有,就应询问。
- 按 EDIT-PRESERVATION DELIVERY 格式报告:说明修改了什么、保留了什么,以及列出但未处理的范围外观察结果。
修复模式
工作簿包含切片器、图表、保护、结构化引用和溢出公式。最快捷的工具都无法看到它们,因此该模式不是一次调用,而是一系列步骤。
-
先诊断,只读。
python scripts/office_repair.py scan <workbook>此命令会报告结构、ListObject 覆盖范围、带有对应公式的缓存错误单元格、碎片化的数据验证范围,以及功能清单。它不会写入任何内容,运行期间工作簿可以在 Excel 中保持打开。添加
--com,还可以让 Excel 检查循环引用。此步骤需要 Excel,并会以只读方式打开文件。每当 Ane 报告打开文件时出现警告对话框,或数字本应出现却显示为 0 时,都值得运行:循环引用存在于 Excel 在计算时构建的依赖关系图中,而不在存储的 XML 中,因此无论怎么读取文件都找不到它。 -
如实解读扫描结果。 有两种结果看起来是好消息,其实不是:
- Excel 从未打开过的工作簿中没有缓存错误。 文件会缓存上次计算的结果,因此由库写入的工作簿不会缓存任何内容。空白表示“没有缓存内容”,而不是“没有问题”。
- 碎片化的验证范围。 应用于整列的下拉菜单最初是一个区域。插入行后,它会碎裂成单行区域,规则也就只在某些行生效。不会出现任何错误,因为缺少下拉菜单时单元格会显示为空。
-
写入前,先与 Ane 确认修复清单。 列出每个单元格、当前值和新值。扫描结果会给出当前值;不要描述自己尚未读取当前状态的变更。
-
构建载荷并应用。 编写一个包含
{sheet, cell, old, new}的 JSON 列表,然后运行python scripts/office_repair.py repair <workbook> --edits edits.jsonold是一道校验,而不是说明文档:程序会先读取当前公式,若与之不匹配,就跳过该编辑。此过程包含五道防护——带时间戳的备份、逐单元格校验、按原样捕获并重新应用工作表保护、最后才保存一次,以及在保存前抛出异常,从而丢弃部分修复,而不是将其写入。 -
跳过单元格意味着失败,而不是无操作。 这表示载荷已过期或地址有误。命令会以非零状态退出并说明原因。应重新扫描,而不是重新运行并添加
--no-strict;后者会写入匹配的内容并保留其余部分,只适用于有意进行的第二轮修复。 -
对照备份进行验证。
python scripts/office_repair.py verify <workbook> --baseline <workbook>.backup-1.xlsx备份是工作簿修复前状态的唯一可靠记录,因此断言会与备份比较,而不是与记忆中的计数比较。
为何使用 COM,而不是 openpyxl。 这不是偏好问题。openpyxl 没有与切片器、工作表保护和 ListObject 几何结构对应的功能,Excel 也不会认可这些内容。COM 路径需要启动 Excel,耗时约一分钟;openpyxl 写入则会损坏工作簿。
通过 COM 写入日期:赋值序列号,而不是日期对象。 COM 会直接拒绝 datetime.date(TypeError: must be a pywintypes time object),而显而易见的修复方法——传入 datetime——却会悄无声息地造成数据损坏:值会经由 UTC 转换,因此在 UTC+2 的机器上,16.09.2026 00:00 会被存储为 15.09.2026 22:00,dd.mm.yyyy 单元格显示的日期就会提前一天。不会引发异常,写入会报告成功,整列看起来也合乎情理。应改为将 Excel 日期序列号写入 .Value2,然后恢复单元格的 NumberFormat,因为赋入纯数字会丢失日期格式:
EXCEL_EPOCH = datetime.date(1899, 12, 30) # day 0 of the 1900 date system
fmt = ws.Cells(r, c).NumberFormat
ws.Cells(r, c).Value2 = (the_date - EXCEL_EPOCH).days
ws.Cells(r, c).NumberFormat = fmt
序列号不包含时区,因此写入的内容就是存储的内容。验证时应重新打开已保存的文件并比较日期,绝不能只看写入是否未报错就认定成功——在执行写入的 COM 会话中看不到这一天的偏差,只有文件往返后才能发现(已于 2026-09-23 在一份真实的评分工作簿中验证,其中每个完成日期都提前了一天)。
通过 COM 处理 Excel 表格和布局——注意事项
将范围转换为表格时,在已完成的结果工作簿中遇到的 6 个陷阱(2026-10-23,证据见 agent-improvements/handoffs/2025-en-results-excel-tables-spec.md 中 13:53 和 14:40 的更新)。每个问题都悄无声息地出现;每项修复只需一行。
ListObjects.Add会自动调整列宽,并关闭标题换行。 修复:在调用前记录每列的宽度,调用后恢复宽度,并再次对标题行设置WrapText = True。- 对单个字符设置字号不会生效。 对单个字符设置字号后,读回的值仍为 11 pt。修复:按文本段设置字号,使用
cell.GetCharacters(start, length).Font.Size = 10,覆盖整个文本段。 Font.Size读到None并不代表字号混杂。 当单元格的文本段存在任何差异时,会返回None,但每个文本段仍可能都是 9 pt。修复:使用GetCharacters逐段读取字号,切勿从单元格读取。AutoFit会忽略合并单元格。 合并且启用了换行的源行会保留原来的行高。修复:将文本复制到一个备用辅助列中,将该列设为合并单元格的宽度,对该行执行AutoFit,获取行高,然后清空辅助列。- 降序排序表格时,文本会排在所有数字上方。 以文本存储的数字以及
17 of 58这类文本值都会排在顶部。修复:在将范围转换为表格之前,先将它们转换为真正的数字(并将文字移至标签或备注中)。 - 不会失败的验证毫无意义。 修复:先在未更改的文件上运行每个验证脚本一次,确认其结果为 FAIL,再相信 PASS。
规范模式
Ane 在 Word、PowerPoint 或 Excel 中编辑了生成的文件。从现在起,她的文件就是源文件,生成器则是过时副本。我们的工作是让文件系统反映这一事实,而不只是口头确认。
-
提取并比较。
python scripts/office_repair.py diff <edited> --against <generated>提取过程会递归处理组合形状和表格单元格。这一点比听起来更重要:对 .pptx 进行扁平化处理只会返回每张幻灯片的标题,不会返回任何项目符号,因为 python-pptx 会将组合形状报告为一个没有文本的形状,而项目符号位于其子形状中。如果提取出来的演示文稿只有标题,那是提取失败,不是演示文稿本身的问题。 -
区分她的编辑和 Word 的修改。 该命令会标记它认为是 Office 而非 Ane 导致的四类情况:变音符号被悄悄删除、智能引号和破折号自动更正、URL 中意外插入空格,以及看起来像是误触按键、但措辞几乎相同的改写。这些都不是定论——应逐项让她确认。之所以值得询问,是因为两种情况并不对称:将自动更正造成的变化带入规范版本,会使其永久存在且不易察觉;而询问一次有意的编辑,只需她回复一行。
如果编辑后的文件文本块数量不足生成文件的一半,命令还会标记结构性异常。这是 2026-07-22 的特征:重新设计时删除了正文层,只留下标题。
-
将她的修改整理成变更列表,按类别分组,并使用她的说法——说明文档现在写了什么,而不是哪个块的索引发生了变化。对标记出的伪影逐项确认。
-
如果存在
.md镜像文件,就同步它,并添加备注,注明作为规范版本的 Office 文件及日期。如果不存在镜像文件,则不要创建。 -
停用生成器。
python scripts/office_repair.py archive <generator>.py --canonical <edited file>这会在文件前置一个保护机制:不加--force就拒绝运行;使用--force时,如果运行过程触碰规范文件,则会逐字节恢复该文件。采用代码而不是注释,是因为注释无法阻止任何操作;需要防止的是未来有人出于好意运行python gen_x.py。重复运行不会产生任何变化。
不要把她的修改合并回生成器。 这看似理所当然,实际上是错的:生成器可以复现文本,却无法复现 Ane 在 Word 中进行的格式层面修改;合并后得到的脚本看起来是最新的,却会悄悄重新生成更差的文件。应将其归档。若某项修改确实应属于生成器,因为它还会为另一份文档再次生成,就明确说明,并将其作为单独的工作处理。
验证计划
每种模式都会在开始操作前说明检查方式,并通过重新打开写入的文件来执行检查:
| 模式 | 检查内容 |
|---|---|
| scan | 只读——不写入任何内容。报告功能清单和发现的数量。 |
| repair | 从 Excel 读回每个单元格的替换结果,然后对照修复前的清单执行 assert_inventory_unchanged,再根据备份执行 verify --baseline:表格涵盖其行、没有缓存错误、没有丢失任何功能。 |
| diff | 只读。报告两边的块数量,并明确列出每个标记的伪影,包括 Ane 确认是有意为之的那些。 |
| archive | 运行归档的生成器。它必须以非零状态码退出,并且规范文件必须保持逐字节一致。断言保护机制的源代码文本是在测试错误的对象——仅存在于文档字符串中的拒绝行为,正是该模式要防止的失败。 |
python scripts/test_office_repair.py 用于检查驱动程序本身:变更列表、伪影启发式规则,以及生成器保护机制是否确实通过子进程拒绝运行。Office 行为由工作目录中的 tests/test_officeops.py 覆盖。
适用范围
- Word 审阅流程——阅读批注、修订、术语表和源文件附录——属于
office-review-pass。此技能用于修复 Excel 文件并完成规范交接。 - 创建新的品牌文件属于
ane_package.reporting;编制 ToR 属于tor-procurement;对文案进行只读 QA 判定属于check-deliverable。 - 两种 COM 路径都需要 Windows、已安装 Excel 和 PowerShell 5.1——绝不能使用 pwsh 7,其对 null 方法的处理会导致 Excel 路径出错。本 Python 环境未安装
win32com,因此不能采用这条路径。 PackageNotFoundError表示 Excel 中有打开的句柄,并不代表文件已损坏。告诉 Ane 她的工作簿已损坏之前,先用zipfile测试。- 在 Web 容器中,
diff和archive路径仍然可用;scan --com和repair不可用。