mcpField
E
encore-migrate

encore-migrate

github.com/encoredev开发30739 次安装2 天前更新
代码仓库

关于 encore-migrate

encore-migrate 是将现有后端逐步迁移到 Encore.ts 或 Encore Go 的技能,适用于不同语言和框架的项目。它通过分阶段发现、规划和迁移,并用计划文件记录单元与进度,便于跨会话继续工作。

主要功能

  1. 01支持多种源语言和框架
  2. 02按迁移单元分阶段推进
  3. 03分析后端实体与依赖
  4. 04生成摘要及单元计划文件
  5. 05支持按进度恢复迁移

适用场景

  1. 01盘点现有后端服务与 API
  2. 02将系统拆分为迁移单元
  3. 03按依赖顺序迁移后端功能
  4. 04跨会话追踪迁移进度

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/encoredev/skills --skill encore-migrate

安装后在 Claude Code 中输入/encore-migrate确认已加载。

详细介绍

迁移到 Encore

此技能将指导你逐个迁移单元,将任何现有后端应用迁移到 Encore。它支持任何源语言或框架,目标为 Encore.ts 和 Encore Go。迁移过程中,会在 Encore 项目根目录创建 migration-plan.md 摘要文件和 migration-plan/ 目录,其中包含每个单元的详细文件,以便跨会话跟踪进度。此技能不包含 Encore 代码示例——所有 Encore 特定实现均由相应语言的专用技能负责。

阶段检测

开始前,先确定要进入的阶段:

  • Encore 项目目录中不存在 migration-plan.md → 从阶段 1:发现开始
  • 存在 migration-plan.md,但不存在 migration-plan/ 目录 → 从阶段 2:规划继续(发现已完成,但尚未编写详细文件)
  • migration-plan/ 目录存在未完成的单元(摘要中任一单元的状态为 pending 或 in progress)→ 从阶段 3:迁移继续
  • 摘要中所有单元的状态均为 migrated、skipped 或 manual validation needed → 进入阶段 4:完成
自主模式

此工作流中的确认暂停点(阶段 1 确认单元、阶段 2 批准第一个单元、阶段 3 选择每个后续单元)为默认行为。如果用户要求不间断地完成迁移(“直接迁移”“全部处理”“别问了”),则跳过这些暂停点:

  • 对未定事项采用合理的默认选项(参见阶段 1 中的“关键决策”),并在编写代码前用简短列表告知用户每项决定
  • 在 migration-plan.md 的 ## Decisions 部分记录每个默认选项,以便后续重新审视
  • 对源系统发起可能产生副作用的 HTTP 调用前仍须询问,且绝不修改源系统
  • 将实体标记为 migrated 前仍须逐一验证
恢复迁移(阶段 3)

当 migration-plan.md 和 migration-plan/ 均存在且有未完成单元时:

  1. 阅读 migration-plan.md(仅摘要——不要读取所有详细文件)
  2. 向用户报告当前状态——例如:“7 个单元中已有 3 个完成迁移,下一个建议处理:billing(它的所有依赖项均已迁移)”
  3. 询问用户接下来想处理什么,并根据计划中的依赖顺序提供建议

阶段 1 — 发现

1. 收集信息

向用户询问:

  • 源系统路径(要迁移的现有代码库)
  • 源系统运行时的本地 URL(如适用——后续进行 HTTP 对比验证时需要)
  • 目标语言: Encore.ts 或 Encore Go
2. 分析源代码库

阅读源代码库并清点所有实体:

类别查找内容
服务 / 模块 / 领域不同的有界上下文、可独立部署的单元、路由分组
API 端点方法、路径、处理函数、请求 / 响应结构
数据库类型(Postgres、MySQL 等)、表、模式、迁移文件
Pub/Sub 主题和订阅主题名称、发布者、订阅者、消息结构
Cron 作业 / 定时任务计划表达式、处理函数
Auth 中间件 / 处理程序身份验证策略、令牌验证、会话管理
密钥 / 环境变量所有引用的环境变量和密钥,并注明哪些是敏感信息
全局 HTTP 行为CORS 设置、全局路径前缀、全局中间件、自定义错误过滤器 / 错误响应正文
现有测试测试文件、覆盖的实体、所用测试框架
前端代码React/Vue/Angular 组件、静态 HTML、CSS、客户端 JavaScript——这些不在范围内
3. 识别前端代码

全栈仓库和单体仓库通常会混合后端和前端代码。迁移仅针对后端,前端代码不在范围内。

检测前端目录并将其标记为不在范围内。 常见指标:

模式示例
专用前端目录frontend/、client/、web/、app/(其中包含 React/Vue/Angular 时)、src/components/、public/
前端配置文件next.config.js、vite.config.ts、nuxt.config.ts、angular.json、.svelte-kit/、remix.config.js
软件包依赖项package.json 中的 react、vue、@angular/core、svelte

标记应当迁移的框架服务端代码。 有些前端框架包含后端逻辑,其中包含 API 端点、数据库查询或服务端业务逻辑:

框架服务端代码位置查找内容
Next.jspages/api/、app/*/route.tsAPI 路由处理程序——这些是后端端点
Remixapp/routes/*.tsx(导出 loader/action)loader 和 action 函数包含服务端逻辑
Nuxtserver/api/、server/routes/服务端 API 路由
SvelteKitsrc/routes/+server.ts、+page.server.ts服务端端点和加载函数
Astrosrc/pages/*.ts(非 .astro 文件)API 端点

如果发现框架服务端代码,请询问用户如何处理。 并非所有服务端代码都应该迁移到 Encore——有时,精简的后端层(BFF、身份验证代理、SSR 数据获取)应与 Encore 后端一起保留在前端框架中。

向用户说明发现的内容并询问:

“我在你的 应用中发现了 个服务端路由(例如,pages/api/users.ts、app/billing/route.ts)。这些路由包含可能迁移到 Encore 的后端逻辑,但有些团队更愿意在前端框架中保留精简的服务端层,用于 SSR 数据获取或 BFF 代理等功能。你希望:

  1. 全部迁移——将所有服务端路由迁移到 Encore
  2. 迁移部分路由——我会列出这些路由,由你选择要迁移的路由
  3. 全部保留在 中——仅迁移独立的后端代码”

根据用户的选择:

  • 全部迁移: 将后端逻辑提取到迁移单元中。前端渲染代码不在范围内。在迁移计划中注明哪些源文件同时包含前端和后端代码。
  • 迁移部分路由: 列出服务端路由供用户选择。将选中的路由纳入迁移单元,其余路由标记为不在范围内。
  • 全部保留: 将所有框架服务端代码与前端代码一并标记为不在范围内。仅将独立的后端代码(Express 路由、独立 API 服务等)纳入迁移单元。

向用户报告: 列出所有检测到的前端目录,以及对框架服务端代码作出的决定。例如:“我在 app/ 中发现了一个 Next.js 前端——React 组件不在范围内。你选择将 pages/api/ 中 12 个 API 路由里的 8 个迁移到 Encore,并将 4 个精简代理路由保留在 Next.js 中。”

4. 确定关键决策

某些选择会影响每个单元。请在规划前明确这些问题,并将答案记录在 migration-plan.md 的 ## Decisions 部分:

  • 行为目标: 旧代码库往往存在 Bug(缺少所有权检查、字段从未保存、筛选条件错误)。询问用户,是要完全复现源代码的行为,还是修复明显的 Bug。如果源代码实现了已发布的 API 契约(OpenAPI 规范、RealWorld、合作伙伴集成),通常应按该契约进行修复。无论选择哪种方案,都要在单元的 Notes 列中列出每项有意做出的差异。
  • 错误响应格式: Encore 的类型化 api() 端点始终以 {code, message, details} 的格式返回错误,且请求验证失败时始终返回 HTTP 400。如果客户端依赖不同的错误响应正文(例如 {"errors": {...}})或状态码(例如 422),有两种选择:接受 Encore 的格式(字段错误可放在 details 中),或对受影响的路由使用 api.raw,但这样就无法使用类型化请求和生成的客户端。询问用户倾向哪种方案。
  • CORS: 如果源代码启用了 CORS,请将其转换为 encore.app 中的 global_cors,而不要依赖 Encore 的默认设置。
5. 将实体分组为迁移单元

使用以下优先级启发式方法,将发现的实体分组为迁移单元:

  1. 现有服务边界 — 如果源应用已有服务、模块或包,请以此作为划分单元的起点
  2. URL 路径前缀 — 将共享路径前缀的端点分组(例如 /users/*、/billing/*)
  3. 共享数据库表 — 读写相同表的端点应归为一组
  4. 共享类型/模型 — 共享请求/响应类型或领域模型的端点应归为一组

分组规模: 每个迁移单元以包含 5–15 个端点为宜。如果某组超过约 15 个端点,建议进一步拆分(例如 users-crud 和 users-admin)。如果某组少于 3 个端点,可考虑与相关分组合并。

横切关注点应单独划分为迁移单元:身份验证、密钥和独立基础设施(发布/订阅主题、与任何服务都没有紧密耦合的定时任务)都应作为单独的单元,因为它们属于不同的依赖层级。

对于没有明确边界的单体应用: 先按 URL 路径前缀分组,然后询问:“这些分组是按 URL 路径划分的——您希望按领域重新组织吗?”

6. 展示迁移单元

通过摘要表向用户展示迁移单元:

单元端点数据库表其他复杂度

列出总数(例如,“7 个迁移单元,涵盖 42 个端点和 3 个数据库”)。评估每个单元的整体迁移复杂度:

  • 低 — 有直接对应的 Encore 实现,映射简单
  • 中 — 需要重构,或只有部分 Encore 实现可对应
  • 高 — 没有直接对应的实现,需要重新设计或自定义解决方案

如果用户想在确认前查看单元包含的内容,可主动提出展示任一单元的详细信息。

7. 展示代码预览

选择 2–3 个有代表性的实体(从不同单元中选取简单和复杂的实体),展示一段简短的“迁移前后”预览,分别呈现源代码当前的样子和 Encore 版本的样子。使用相应的语言专用技能来指导预览。预览应简短——每段预览展示一个端点、一个查询或一个主题声明即可。

8. 请用户确认

请用户确认迁移单元是否正确。具体询问:

  • “是否有我遗漏的服务、端点或其他实体?”
  • “您希望拆分、合并或重命名其中任何迁移单元吗?”
  • “您希望将任何内容排除在迁移之外吗?”
9. 根据需要迭代

如果用户指出有遗漏的实体,或希望调整分组边界,请更新迁移单元并重新展示摘要表。重复此过程,直到用户确认迁移单元准确无误。

阶段 2 — 规划

1. 检查是否已有 Encore 项目

检查目标路径下是否已有 Encore 项目(查找 encore.app 文件)。如果有,请向用户确认这是否是正确的项目。如果没有,通过调用 encore-getting-started 技能(Go 项目则调用 encore-go-getting-started)帮助创建项目。

2. 收集目标信息

向用户询问:

  • Encore 项目的路径(迁移后的代码所在位置)
  • Encore 应用运行的本地 URL(默认值:http://localhost:4000)
3. 确定依赖顺序

根据依赖关系排列迁移单元。按以下层级顺序执行:

  1. 密钥 / 配置 — 无依赖,是其他所有内容所需的
  2. 数据库 — 服务使用数据库前,必须先准备好架构和迁移
  3. 身份验证 — 需要先实现身份验证处理程序,再实现受保护的端点
  4. 叶子单元 — 没有跨服务依赖的单元
  5. 依赖单元 — 依赖已迁移单元的单元
  6. Pub/Sub 主题和订阅 — 通常依赖于已就绪的服务
  7. 定时任务 — 通常依赖于服务端点

在每个层级内,优先建议最简单的单元(端点最少、架构最小、复杂度最低)。

4. 编写 migration-plan.md(摘要)

使用下方“migration-plan.md 格式”部分中的模板,在 Encore 项目根目录编写 migration-plan.md 摘要文件。将所有迁移单元的状态设为 pending。

5. 编写详细文件

在 Encore 项目根目录创建 migration-plan/ 目录。使用下方“详细文件格式”部分中的模板,为每个迁移单元编写一个详细文件。每个文件命名为 migration-plan/<unit-name>.md。

6. 提议第一个单元

提议第一个迁移单元,并根据依赖顺序说明应优先迁移它的原因。等待用户批准后再继续进入阶段 3。

阶段 3 — 迁移(循环)

1. 确定下一个单元

读取 migration-plan.md(仅摘要),并根据依赖顺序确定下一个状态为 pending 的迁移单元。

2. 建议并确认

建议下一个要迁移的单元,并说明选择它的原因(例如:“此单元不依赖任何尚未迁移的单元”或“必须先准备好数据库,才能迁移使用它的服务”)。询问用户是要继续迁移此单元,还是选择其他单元。

3. 加载单元详细信息

读取所选单元的详细文件(migration-plan/<unit-name>.md)。不要读取其他单元的详细文件。

4. 迁移各实体

对于单元中的每个实体:

a. 实现

根据实体类型和目标语言,调用相应的语言专属技能:

正在迁移的内容Encore.ts 技能Encore Go 技能
服务结构encore-serviceencore-go-service
API 端点encore-apiencore-go-api
身份验证encore-authencore-go-auth
数据库 + 迁移encore-databaseencore-go-database
Pub/Sub 主题和订阅encore-pubsubencore-go-pubsub
定时任务 / 计划任务encore-cronencore-go-cron
对象存储 / 文件上传encore-bucketencore-go-bucket
缓存(Redis)encore-cacheencore-go-cache
密钥 / API 密钥 / 凭据encore-secretencore-go-secret
Webhook(Stripe、GitHub 等)encore-webhookencore-go-webhook
测试encore-testingencore-go-testing
b. 迁移测试

如果源实体有关联的测试,使用相应的测试技能(encore-testing 或 encore-go-testing)迁移测试。调整测试断言,使其符合 Encore API 模式。如果源实体没有测试,请在详细文件中注明。

c. 验证

每个实体在标记为 migrated 前,都要经过三个验证层。每个实体都必须完成所有适用层的验证。

第 1 层:测试迁移(主要)
  • 迁移实体时,同时迁移其关联的测试
  • 使用 encore-testing 技能(Go 项目使用 encore-go-testing)实现测试
  • 运行测试——测试必须通过,才能将实体标记为 migrated
  • 如果源实体没有测试,请在计划中注明“源代码没有测试”,并依靠其他层进行验证
第 2 层:HTTP 对比(仅限端点,尽力而为)

当两个系统都在本地运行时,分别调用源系统和 Encore 应用中的同一端点,然后进行比较:

  • HTTP 状态码——必须一致
  • 响应正文结构——键和形状必须一致(时间戳或 ID 等动态数据的值可以不同)

以下情况跳过此层:

  • 端点需要 Agent 无法获取的身份验证凭据(询问用户,允许跳过)

如果连接任一系统的请求失败,请让用户启动应用后再重试。不要默默跳过——用户可能只是忘了启动应用。

进行任何可能产生副作用的 HTTP 调用前,始终先询问用户。

第 2 层替代方案:契约测试套件

如果源系统实际上无法运行(数据库引擎不可用、工具链太旧而无法安装、存在外部依赖),或者目标行为有意偏离源系统,则与源系统进行 HTTP 对比没有意义。如果 API 有外部契约测试套件(Hurl、Postman/Newman、Bruno、针对 OpenAPI 规范运行的 Schemathesis,或使用方的集成测试),请改为针对 Encore 应用运行该套件:

  • 运行完整套件并继续执行失败项(例如 hurl --test --continue-on-error --report-json),以便显示每项失败断言,而不只是每个文件中的第一项
  • 对失败进行分类。区分已记录决策(例如错误响应格式)导致的预期差异与实际行为缺陷,并修复这些缺陷
  • 在 Validation Log 中记录通过/失败的数量及分类

在计划中注明跳过与源系统进行 HTTP 对比的原因。

第 3 层:完成前验证关卡

在将任何实体标记为 migrated 之前,Agent 必须有当前会话中的最新证据:

  • 显示通过数量和退出代码的测试命令输出,或
  • 显示匹配结果的 HTTP 对比结果,或
  • 用户明确批准跳过验证

规则:

  • 不要使用“应该能用”“看起来正确”或“似乎没问题”之类的说法——只能使用有证据支持的结论
  • Agent 必须准确说明验证了什么,以及输出结果是什么
  • 如果证据不足,将实体标记为 manual validation needed,而不是 migrated
  • 之前会话的过期证据不算数——恢复工作时重新运行验证
d. 更新详细文件

在单元的详细文件(migration-plan/<unit-name>.md)中更新实体状态,并在该文件的 Validation Log 表格中记录验证证据。

e. 更新摘要

单元中的所有实体都完成后,在 migration-plan.md 中将单元状态更新为 migrated。如果仍有实体待处理,则将单元状态设为 in progress。

5. 继续或暂停

完成一个单元后,询问“你接下来想迁移什么?”,并根据依赖顺序建议下一个单元。

6. 批量处理

默认一次处理一个单元。如果用户说“继续”“全部处理”或类似的话,可以批量处理多个单元,但仍须在将每个实体标记为已迁移之前分别进行验证。

阶段 4 — 完成

当 migration-plan.md 中的所有单元都标记为 migrated、skipped 或 manual validation needed 时:

  1. 根据 migration-plan.md 提供最终摘要:
    • 已迁移的单元总数
    • 标记为 manual validation needed 的单元——阅读对应的详细文件,并列出需要关注的实体及原因
    • 已跳过的单元(列出单元及原因)
  2. 建议最后再运行一次完整测试套件,以发现任何集成问题
  3. 说明仍需人工关注的手动验证事项
  4. 如果源系统包含前端代码,建议使用 encore-frontend 技能将前端重新连接到新的 Encore 后端(生成类型化 API 客户端、配置 CORS、更新基础 URL)
  5. 建议在用户确认对迁移结果满意后,从项目中移除 migration-plan.md 和 migration-plan/

提问

在以下情况下,采取行动前先询问用户:

  • 服务边界不明确——例如:“这些路由文件可以拆成 1 个服务,也可以拆成 3 个——你希望如何划分?”
  • 不存在合适的 Encore 等效方案——例如 Redis 缓存层、自定义中间件链、WebSocket 处理程序
  • 存在多种有效的迁移策略——说明各种选项及其权衡
  • 进行任何可能产生副作用的 HTTP 调用之前——务必先询问
  • 源代码含义不明确——当 Agent 无法确定代码的作用时,应先询问,而不是猜测
  • 源系统似乎已发生变化——如果详细文件引用的文件已不存在或发生了重大变化

保护源系统

迁移期间绝不能修改源系统。请遵守以下规则:

  • 绝不修改源文件——只读取,不编辑
  • 绝不删除源文件——即使迁移已完成也不得删除
  • 绝不写入源目录——所有输出都写入 Encore 项目
  • 绝不对源系统运行破坏性命令(如删除表、删除队列等)
  • 对源系统进行任何会更改状态的 HTTP 调用(POST、PUT、DELETE)之前,先征得用户同意

如果用户要求“清理”或“移除”旧系统,必须先明确确认,然后才能采取任何行动。源系统可能仍在为生产流量提供服务。

边缘情况

在不同单元之间移动端点

如果用户发现某个端点应属于其他迁移单元:

  1. 从源单元的详细文件中移除该端点对应的行
  2. 将其添加到目标单元的详细文件中
  3. 更新 migration-plan.md 中的端点数量
迁移过程中拆分单元

如果处理某个单元时发现它规模过大:

  1. 为拆分出的部分创建新的详细文件(migration-plan/<new-unit>.md)
  2. 将待处理实体移至新文件(已迁移的实体保留在原文件中)
  3. 将新单元添加到 migration-plan.md 摘要表中
  4. 按依赖顺序插入新单元(位于同一层级,排在原单元之后)
将单体迁移为多个 Encore 服务

一个迁移单元可能对应多个 Encore 服务。详细文件记录源分组,但“Notes”列可以注明目标 Encore 服务。迁移过程中应询问该单元是对应单个服务,还是应拆分到多个 Encore 服务中。

发现后源代码发生变化

如果详细文件引用的文件已不存在,或自发现阶段以来发生了重大变化:

  1. 向用户指出差异
  2. 询问是根据当前状态更新详细文件,还是跳过受影响的实体
  3. 如果选择更新,则重新评估复杂度并相应调整计划

故障排查

迁移期间的常见问题及其解决方法:

问题原因解决方法
服务之间导入错误服务之间直接导入改用 ~encore/clients(TS)或服务客户端包(Go)
数据库迁移失败SQL 语法不兼容确认 Encore 使用 PostgreSQL,并调整 MySQL/SQLite 语法
未收到 Pub/Sub 消息未注册订阅确保在包级别声明订阅,而不是在函数内部声明
定时任务未触发调度表达式无效Encore 使用标准 cron 表达式,请检查语法
encore run 基础设施报错在函数内部声明了基础设施将所有基础设施声明移至包级别
源项目与 Encore 的响应不同缺少业务逻辑或错误处理方式不同仔细比较响应结构,并检查边缘情况
无法验证端点需要身份验证或存在副作用向用户索取测试凭据,或标记为 manual validation needed
客户端需要 422 或自定义错误正文Encore 的类型化端点使用固定的错误封装,并且验证错误会返回 400将其记录为一项决策:接受 Encore 的格式,或对这些路由使用 api.raw
迁移后浏览器客户端无法运行源项目全局启用了 CORS在 encore.app 中添加 global_cors
无法运行源项目进行 HTTP 对比工具链过旧或基础设施不可用改用契约测试套件(参见第 2 层替代方案)

migration-plan.md 格式

使用此确切模板创建摘要计划文件。填写发现阶段获取的值。

# Migration Plan

## Source System
- **Path:** <source system path>
- **URL:** <source system local URL>
- **Framework:** <detected framework>
- **Language:** <detected language>

## Frontend (Out of Scope)
- **Detected:** <Yes/No>
- **Directories:** <list of frontend directories, or "None">
- **Framework:** <frontend framework if detected, or "N/A">
- **Note:** <any framework server-side code that WAS included in migration units>

## Target System
- **Path:** <encore project path>
- **URL:** <encore local URL>
- **Type:** Encore.ts | Encore Go

## Decisions
- **Behavior target:** <reproduce source exactly | fix bugs toward <contract>>
- **Error format:** <Encore native | api.raw for <routes>>
- **CORS:** <source setting → encore.app global_cors>
- <any other default chosen in autonomous mode>

## Migration Units

| Unit | Endpoints | DB Tables | Other | Complexity | Status |
|------|-----------|-----------|-------|------------|--------|

## Dependency Order
1. <ordered list of migration units>

状态值: pending、in progress、migrated、skipped、manual validation needed

复杂度值: Low(直接对应)、Medium(需要重构)、High(需要重新设计)

详细文件格式

在 migration-plan/<unit-name>.md 为每个迁移单元创建一个文件。使用此确切模板:

# Migration Unit: <unit-name>

## Source
- **Files:** <list of source files in this unit>
- **Depends on:** <other migration units, with their current status>

## Endpoints
| Endpoint | Method | Path | Status | Notes |
|----------|--------|------|--------|-------|

## Database
| Table | Complexity | Status | Notes |
|-------|------------|--------|-------|

## Tests
- **Source tests:** <test files and count>
- **Migrated:** <count of migrated tests>

## Validation Log
| Entity | Tests | HTTP Match | Evidence | Status |
|--------|-------|------------|----------|--------|

并非所有章节都是必需的——省略不适用于特定单元的章节(例如,密钥单元不会包含端点或数据库章节)。

评论

支持 Markdown