关于 refactor-module
refactor-module 用于将范围过宽的 .plain 模块重组为 requires 链,同时保留所有规格的行为与顺序。它指导用户完成拆分规划、确认、迁移和验证。
主要功能
- 01分析规格与依赖关系
- 02规划语义模块分组
- 03保留时间顺序
- 04分配并导出概念
- 05迁移测试并验证
适用场景
- 01拆分包含无关功能的模块
- 02规划安全的模块依赖链
- 03迁移规格并保留其行为
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ plainlang/ plain-forge --skill refactor-module安装后在 Claude Code 中输入/refactor-module确认已加载。
详细介绍
重构模块
先加载规则。 在读取、编辑或编写任何 .plain 文件之前,请调用 load-plain-reference 技能,并阅读它指引你查看的规则文件。只有当本次对话中已经运行过 load-plain-reference 时,才可跳过此步骤。
适用情形
- 一个模块包含许多涉及多个不同领域或功能区的功能规格。
- 模块中交织着互不相关的关注点,因此难以理解。
- 模块的范围过于宽泛,难以添加新功能。
- 用户明确要求拆分或重新组织模块。
指导原则
重构只调整结构。重构后各模块的组合行为必须与原模块完全一致。不得添加、删除或削弱任何功能规格。拆分应基于语义——将相关规格归为一组——而非改变行为。
输入
要重构的 .plain 文件,以及用户提供的分组指导(或请用户确认你提出的分组方案)。
阶段 1 — 分析当前模块
- 通读整个
.plain文件——包括 frontmatter、定义、实现要求、测试要求和所有功能规格。还要阅读所有import和requires依赖链,以了解完整上下文。 - 盘点功能规格。 列出每项功能及其 func-id,并注明它引用的
:Concepts:。建立依赖关系图:哪些规格依赖较早的规格(引用了它们引入的行为或状态)。 - 盘点定义。 对每个概念,注明哪些功能规格引用了它。找出多个逻辑分组都会用到的概念,以及仅在某个分组内使用的概念。
- 确定逻辑分组。 寻找自然的切分点——共享概念、属于同一连贯领域或描述同一功能区的功能规格集群。常见分组包括:
- 数据模型设置和 CRUD 操作
- 业务逻辑和处理规则
- UI 屏幕和导航
- 集成和外部接口
- 统计、报告和分析
- 验证分组的独立性。 每组规格在时间顺序上都应形成连续或近乎连续的区块。如果某组规格分散在其他组之间,可能需要调整拆分边界。
阶段 2 — 规划拆分
进行任何更改前,先向用户展示拟议的拆分方案,并获得明确确认。
2a. 定义模块链
模块组成一条 requires 依赖链,以保留功能规格原有的时间顺序。链中的第一个模块包含最早的规格;后续每个模块都 requires 前一个模块,并添加下一个逻辑分组。
original-module
↓ splits into
module-group-a (no requires — this is the chain root)
module-group-b (requires: module-group-a)
module-group-c (requires: module-group-b)
如果原模块已有 requires 或 import 依赖,第一个新模块应继承这些依赖。
2b. 将功能规格分配到模块
每项功能规格只能放在一个模块中。分配时必须遵循时间顺序:
- 每个模块内的规格,其相对顺序必须与原模块一致。
- 沿依赖链排列的各模块必须保持顺序:模块 A 中的所有规格都必须早于模块 B 中的所有规格,模块 B 中的所有规格又必须早于模块 C 中的所有规格。
- 某项规格不能移到其所依赖的规格之前的模块中。
2c. 将定义分配到模块
对每个概念,确定它所属的位置:
| 概念的使用范围 | 放置方式 |
|---|---|
| 仅用于一个模块的规格中 | 在该模块中定义 |
| 在依赖链中的多个模块中使用 | 在首次使用该概念的模块中定义,并将其添加到该模块的 exported_concepts |
| 已在某个导入模板中定义 | 保留在导入模板中——无需更改 |
如果许多概念在所有模块中共享,可考虑创建一个新的导入模块(模板)来存放它们。这样可以避免 exported_concepts 列表过长,并集中管理定义。请使用 create-import-module 技能。
2d. 分配实现要求和测试要求
- 适用于所有模块的要求应放在共享导入模板中。如果已有此类模板,请将它们保留在其中。
- 特定于单个模块所属领域的要求应移至该模块。
- 如有疑问,请将要求保留在共享模板中——各模块之间重复内容,总比模板范围稍广要糟。
2e. 呈现计划
向用户总结:
- 拆分后模块的数量及其名称
- 每个模块包含哪些功能规格(按 func-id 列出)
requires链的顺序- 是否会创建新的导入模块
- 每个模块导出哪些概念
- 确认已涵盖所有原始规格(没有遗漏)
继续操作前,必须获得用户明确确认。
第 3 阶段 — 执行拆分
按顺序从链根开始创建新模块。
3a. 创建或更新导入模板(如有需要)
如果要将共享定义或要求移至新的导入模板,请先使用 create-import-module 技能创建该模板。
3b. 创建链根
创建链中的第一个模块:
- 设置 YAML frontmatter——继承原始模块的
import引用。为下游模块需要的概念添加exported_concepts。 - 添加
***definitions***部分,包含此模块规格所用的概念以及需要导出的概念。 - 添加特定于此模块的
***implementation reqs***和***test reqs***(如果导入模板未完全涵盖这些要求)。 - 添加分配给此模块的
***functional specs***,保留其原始顺序和确切措辞。 - 将已移动功能的测试纯文本文件中的
module:键设为新模块名称,并将文件移至该模块的标准位置;func-id 和场景文本保持不变。
3c. 创建后续模块
对链中的每个后续模块:
- 将
requires设为指向链中的上一个模块。如果使用共享模板,则设置import。 - 为链中后续模块需要的概念添加
exported_concepts。 - 为此模块的本地概念添加
***definitions***。不要重新定义通过requires(已导出)或import可用的概念。 - 添加特定于此模块的
***implementation reqs***和***test reqs***。 - 添加分配给此模块的
***functional specs***,保留其原始顺序和确切措辞。 - 以相同方式移动已移动功能的测试纯文本文件。
3d. 处理原始模块文件
创建所有新模块后:
- 如果原始模块将被完全替代(所有规格均已移出),请删除它或重命名,以免造成混淆。
- 如果其他模块依赖原始模块,则更新这些模块,使其改为
require新链中的最后一个模块——该模块累积的代码和此前的功能规格涵盖了整条链。不过,导出的概念不会传递:如果下游模块引用了现在由新链中较早模块导出的概念,则它也必须在requires中列出该较早模块。
第 4 阶段 — 验证
4a. 完整性检查
这是最关键的验证。逐一检查原始模块中的每个功能规格,并确认它恰好出现在一个新模块中。不得遗漏、弱化、修改任何内容,也不得将内容留作隐含。
检查清单:
- 所有新模块中的规格总数与原始规格总数相同
- 每项规格的文本与原文完全一致(不得改写),且保留其 func-id
- 每项功能的测试纯文本文件均已随之移动,
module:已更新,场景内容保持不变
4b. 时间顺序检查
验证沿 requires 链从根到叶依次阅读时,所有功能规格的时间顺序与原始顺序完全一致。任何规格都不得出现在其所依赖的规格之前。
4c. 检查概念是否可用
对于每个模块,验证:
- 其规格中引用的每个
:Concept:都在本地定义、通过import导入,或可通过requires中所列模块的exported_concepts获取 -
exported_concepts包含下游模块所需的每个概念 - 模块之间没有概念名称冲突
4d. 验证每个模块的结构
对于每个新模块:
- 至少包含一个功能规格和一个实现需求
- YAML 前置元数据格式正确
-
requires和import路径正确 - 模块文件位于仓库根目录(不在
template/中) - 规格与语言无关
- 所有外部接口均明确列出
4e. 阅读所有新模块
完整阅读每个新的 .plain 文件,并向用户展示最终结构以供审批。如果用户要求修改,请应用修改并重新验证。
常见问题
拆分过度
创建许多仅包含 1–2 条规格的小模块,会增加开销,却不能提升清晰度。每个模块以包含 3–10 条功能规格为宜,并围绕一个连贯的主题分组。
破坏时间顺序
建立基础行为(入口点、数据库初始化、核心 CRUD)的规格,即使涉及多个领域的概念,也必须留在链的根模块中。只有在规格可以清晰分离时才进行拆分。
忘记设置 exported_concepts
如果模块 B 中的规格引用了模块 A 定义的概念(且 B 依赖 A),则该概念必须包含在 A 的 exported_concepts 中。缺少导出项会导致渲染失败。
拆分时修改规格文本
这是重构,不是重写。规格文本必须原样复制。如果某条规格需要改写才能适用于新模块上下文,这说明拆分边界不合理——应调整分组方式。
丢失测试 plain 文件
测试 plain 文件通过 module: 和 func_id: 与其功能关联。当某项功能移至新模块时,其 module: 键也要更改,并将文件移至新模块约定的位置;否则渲染器会报告找不到匹配功能的条目。
验证清单
- 执行拆分前,用户已确认拆分计划
- 所有原始功能规格都出现在且仅出现在一个新模块中——没有遗漏
- 规格文本与原文完全一致——没有改写
- 测试 plain 文件随对应功能移动,并重新绑定到新模块
-
requires链中保留了时间顺序 - 每个模块中引用的
:Concept:都可用(本地定义、导入或导出) - 每个模块都正确设置了
exported_concepts - 没有概念名称冲突
- 每个模块至少包含一个功能规格和一个实现需求
-
requires链的顺序正确(根 → 叶) - 原模块已移除或更新
- 下游模块(如有)已更新为依赖链末端模块,以及其引用了导出概念的任何更早链上模块
- 用户已批准最终结果