
Claude Code
Claude Code 大规模语言迁移实战:六阶段流程、脚本约束与完整示例
用 Claude Code 推进整库语言迁移的参考教程:拆解六阶段人工签核流程、八个提示词与六个脚本的分工、磁盘产物清单,以及脚本真正会拦截的失败条件,并给出一个可跑通的最小示例。
把一整个代码库从一种语言换到另一种语言,难点通常不在翻译本身,而在于「翻到哪了」「翻得对不对」「有没有偷偷改掉架构」。Claude Code 配合一套迁移工具包,可以把这件事拆成有人工签核的批次:每个阶段结束由人确认,再由人启动下一个提示词,脚本负责把文件依赖、路径替换、输出存在性、命令拦截和构建日志这些机械环节固定下来。本文按流程、产物、脚本约束、完整示例的顺序整理,适合准备做整库迁移、又希望保留架构与数据结构的工程团队参考。价格、配额、版本等信息请以官网当前信息为准。
准备工作
这套流程默认的立场是:保留架构、数据结构和文件边界,只替换语言。它面向的是全面迁移——所有文件都要搬过去,旧语言最终消失。增量的、中途共存的迁移(例如 JavaScript 逐步转 TypeScript)不在设计目标内。
开始之前需要确认几件事:
- 迁移范围是全面的。如果只想改一部分文件、旧语言还要长期保留,这套流程的出口条件不成立。
- 有可用的测试面。公开接口层面的测试、或者能临时搭起来的验证脚手架,是判断「行为是否一致」的依据。如果既没有公开面测试,也没有第三方语言的测试套件,出口条件会缺失。
- 准备好规则手册的落点。工具包把规则写在
migration/RULEBOOK.md,它是后续所有阶段的判据来源。 - 准备好 Claude Code 的权限配置。工具包提供
templates/settings.json,里面是permissions.deny的拦截模式。 - 确认模型分层策略。写规则和做评审用较大的模型,大批量翻译用较小的模型,这个分层写在
CLAUDE.md里,属于流程的一部分,不是可选项。
工具包本身以提示词、模板和脚本的形式提供,随附的脚本共六个,模板包括规则手册、缺口清单的列定义,以及上面提到的权限配置。仓库自身的说明里明确写着:这些提示词是面向生产迁移的泛化重构,而不是某次真实迁移的逐字副本。
操作步骤
第一步:理解六阶段结构与人工签核点
整个流程分六段:
- 地图与规则
- 规则的压测
- 翻译
- 编译
- 启动
- 行为一致性验证
每一段结束都需要人工签核,下一个提示词也由人来启动。这是这套流程和「让代理一路跑到底」最大的区别:脚本不负责判断阶段是否合格,人负责。
有一个分支需要提前判断:如果要做的是重新设计,规则手册的角色会变成设计文档,第二阶段的「对比测试」不适用。如果公开面测试还在,那么可行性报告之后直接进入第一阶段;如果测试依赖内部实现细节,则先用 prompts/00b 造出判定器,再往下走。
第二步:按角色挑选提示词
可粘贴使用的提示词共八条,各自承担一个角色:
| 文件 | 承担的角色 |
|---|---|
prompts/00 | 可行性评估,包含三个 call 与 Model plan |
prompts/00b | 判定器的创建 |
prompts/01 | 依赖地图,附带两名怀疑视角的评审 |
prompts/02 | 缺口清单 |
prompts/03 | 试点翻译 |
prompts/04 | 评审,两名评审人 |
prompts/05 | 修复者,要求 TODO(port) 的计数与 grep 结果一致 |
prompts/06 | 标记的消化处理 |
注意 prompts/00 里的 Model plan:模型分层如果没写清楚,属于流程违规。分层检查并不能替代模型选择本身。
第三步:用脚本把机械约束固定下来
随附脚本六个,分工如下:
| 脚本 | 作用 |
|---|---|
depmap_python.py | Python 依赖地图,用 ast 取边 |
depmap_ts.mjs | JavaScript / TypeScript 依赖地图,看相对 specifier |
depmap_c.py | C 依赖地图,看 "..." 形式的 include 与同名头文件 |
make_manifest.py | 生成清单 |
queue_runner.mjs | 把还没有输出的文件送进队列 |
build_daemon.sh | 只执行一次构建 |
翻译完成之后,由 queue_runner 挑出还没有产出的文件;调查性构建之后,由 build_daemon 只跑一次构建。队列本身不保留对话记忆——它靠磁盘上文件是否存在来判断进度,这也是它可以随时中断、随时续跑的原因。
第四步:确认磁盘上应该留下什么
完成的单位不是「对话里说完成了」,而是规则手册里命名的输出文件确实存在于磁盘上。主要产物包括:
migration/RULEBOOK.md——规则手册migration/depmap/——目录下含edges.tsv、order.txt、cycles.txtmigration/inventory.tsv——清单migration/manifest.tsv——映射表- 翻译后的目标文件
migration/build-output-rN.txt——构建输出migration/cost-log.tsv——成本记录
第五步:分清「脚本会拦」和「提示词要求」
约束分两类,混淆这两类是常见的踩坑来源。
脚本或配置真正会拦下的:
- 文件之间的依赖,以及文件粒度的循环——三个 depmap 脚本输出边、批次顺序和大小不小于 2 的强连通分量。它们不看内容是否正确。
- 多文件却一条边都没有——三个脚本都会往 stderr 写警告,但退出码仍然是 0,并且照样写出空的
edges.tsv。 - 转换目标路径和原路径相同——
make_manifest.py在target == source时以 exit 1 退出。它比较的是路径字符串,不是结构是否一致。 - 翻译是否完成——
queue_runner.mjs只看输出文件是否存在;它的 verify 只对 0 字节文件报错。 - 循环中的代表性构建,以及命中
templates/settings.json中 deny 模式的破坏性 git 与 Bash 命令。 - 构建执行是否收敛到一处——
build_daemon.sh在树的哈希变化时执行一次--cmd,把标准输出和标准错误保存到migration/build-output-rN.txt。它不做诊断表拆解,也不重跑测试。
提示词要求、但脚本不会拦的:
- 保持结构还是重新设计——写在
prompts/00的三个 call 和规则手册 §0 的立场段落里,没有脚本检测替换。 - 相同架构、相同数据结构、相同文件边界——规则手册 §0 有要求,但没有脚本比较类型、字段、文件划分是否一致。
- 包或 crate 边界的循环——
prompts/01要求「在脚本里压缩」,但随附 depmap 只输出文件路径的强连通分量。 - 依赖地图的过与不足——靠
prompts/01的两名怀疑评审,file:line 的逐条比对由模型完成。 - 缺口清单的覆盖度与行正确性——
prompts/02里wc -l只是行数的收据,没有检查器验证每个站点都有对应行。 - 循环中不编辑规则手册——写在
CLAUDE.md和各提示词里;权限配置的说明文档明确写着这个区别无法用 permission 表达,只能靠评审发现。 - 翻译是否遵守规则——靠
prompts/03的试点和prompts/04的两名评审,契约是「指摘必须引用规则或源码行」,但没有脚本验证引用真实存在。 PORT STATUS里的todos=——prompts/05要求修复者与grep -c 'TODO(port)'对齐,执行者是模型,queue_runner不看这个。而且等式只覆盖TODO(port),BUG(port)和PERF(port)不计入。- 行为是否一致——靠第六阶段和
prompts/00b的判定器,用使用侧测试或临时搭的脚手架;成败记录是RUN-NOTES里的自我申报。
第六步:按需补上自己的失败条件
想保留的层次,光写进规则手册是不会被检查的。要让它真正生效,得加进自己的脚本失败条件里。常见的四个补充方向:
- 除了文件图,再按目标语言的包边界检测循环。
- 把「0 条边」改成 exit 1。
- 在
queue_runner的 verify 里加入尾注条数与TODO(port)/BUG(port)/PERF(port)的一致性检查,而不是只留在提示词里。 - 在 fan-out 之前,由脚本检查
.claude/settings.json的 deny 是否存在,而不是写一句「让代理自己看」。
另外,行为判定器应该在翻译之前先跑一次「用坏输入让它失败」,把这次失败记录下来。prompts/00b 写了这个步骤,但运行记录显示它尚未被执行。
一个完整示例
下面是一个从头到尾能跑通的最小流程,假设目标是把一个小型 Python 库整体迁移到另一种语言,仓库根目录为工作目录。
1. 建立迁移目录并放入规则手册
mkdir -p migration/depmap
cp templates/RULEBOOK.md migration/RULEBOOK.md
cp templates/settings.json .claude/settings.json
规则手册 §0 要写清立场:是保持架构与数据结构,还是重新设计。这一步的结论决定后面是否使用第二阶段的对比测试。
2. 生成依赖地图
python scripts/depmap_python.py --root . --out migration/depmap
产出三个文件:edges.tsv 是边,order.txt 是批次顺序,cycles.txt 是大小不小于 2 的强连通分量。如果仓库里明明有多个文件却一条边都没有,stderr 会出现警告,但退出码仍是 0,edges.tsv 会是空的——这种情况需要人工介入,不能当成「没有循环」。
3. 生成清单与映射表
python scripts/make_manifest.py --root . --out migration/manifest.tsv
如果某个条目的目标路径和源路径完全相同,脚本会以 exit 1 退出。这是路径字符串层面的检查,不代表结构真的对应上了。
4. 用队列推进翻译
node scripts/queue_runner.mjs --manifest migration/manifest.tsv --dir migration
队列只把「输出文件还不存在」的条目交出来,不保留对话记忆。中断之后重新执行同一条命令即可续跑。verify 只对 0 字节的输出报错,所以「文件存在但内容不对」不会被这一步拦住,需要靠 prompts/04 的两名评审。
5. 单次构建并留存日志
bash scripts/build_daemon.sh --cmd "make build"
树的哈希发生变化时,它执行一次 --cmd,把标准输出与标准错误写进 migration/build-output-rN.txt。它不拆解诊断表,也不重跑测试。把编译器放进循环之外是有意为之:配置说明里提到,代理会为了迎合编译器而收敛翻译的野心。
6. 人工签核并进入下一阶段
检查规则手册里命名的输出文件是否都在磁盘上,然后由人启动下一个提示词。整个流程的推进权始终在人手里。
注意事项
规模数字要分开读。公开材料里出现的行数、token 数、费用、测试件数,来源并不相同:一部分是作者博客对自己那次运行的自我申报,另一部分是工具包自己跑过的小规模运行记录。工具包本身没有价格表。把博客里的数字当成这套工具包的实测结果,是不成立的。
工具包的运行记录规模很小。已完成的自我验证覆盖的是几个小目标:一个 5 文件 1425 行的库、一个约 730 行的表达式求值器,以及一次 7 文件 2513 行的重译。这不是生产级规模。
运行记录里的已知问题。第一次运行因为比较器缺陷,12 项里有 12 项都是假差异;prompts/00b 标注为尚未自我验证;第二次运行到最后都没有把 settings 装进去;第二、三次运行用的是默认模型。这些都属于自我申报,不是外部复核结论。
deny 不是安全边界。权限配置的说明文档明确写着:确定性的包装器可以绕过这些模式,它「不是安全边界」。没有 deny 的运行,脚本不会阻止。prompts/03 和 prompts/04 允许通过 deviation log 里的显式豁免来放行缺失项。deny 在包装器以外的路径上能守到什么程度,公开资料范围内没有定论。
行数会因数法差 1。规则手册模板里写的行数与 wc -l 的结果可能相差 1,原因是文件末行没有换行符。GitHub 显示的行数包含最后一行,wc -l 数的是换行符个数。核对时留意这一点。
文件边界的处理方式可能和工具包默认不同。有的迁移文档会要求:文件末尾的 @import 不要一对一照搬、alias 要删掉、生成文件写成三行 stub。这类做法与工具包「保持相同文件边界」的默认立场并不一致,需要提前决定采用哪一套。
仓库的维护状态。默认分支只有一次提交,说明文档里写着不看 Issue 和 PR。Issue 功能本身是开启的,但当时没有未关闭的缺陷报告;有一个 PR 正文为空、未合并,内容是给 Java 和 .NET 增加 depmap 的提案,没有进入主分支。许可证文件正文是 Apache License 2.0,而 GitHub 的 license API 当时返回的 spdx_id 是 NOASSERTION,两者显示不一致。
以下条件会让这套模板的机械部分失去出口。做重新设计时,对比测试不适用;做不删除旧语言的增量迁移时,出口条件不成立;既没有公开面测试也没有第三方语言测试套件时,出口条件缺失;类型检查足够便宜、于是去掉 deny 并把第四阶段并入第三阶段时,调查性构建不会被使用;忘记装 settings 时,会在没有护栏的情况下直接跑完。
最后,模型分层要保留:写规则和做评审用较大的模型,大批量翻译用较小的模型。分层检查不会让模型选择这件事消失。