/refactor-safety
当用户要求重构代码(提取组件、合并重复逻辑、重命名变量、优化结构)时触发。提供重构安全检查清单,防止丢失原始上下文。
$ npx -y skills add doccker/cc-use-exp --skill refactor-safety --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/refactor-safety
Context preview
The summary Claude sees to decide when to auto-load this skill.
当用户要求重构代码(提取组件、合并重复逻辑、重命名变量、优化结构)时触发。提供重构安全检查清单,防止丢失原始上下文。
SKILL.md
refactor-safety.SKILL.mdname: refactor-safety
description: 当用户要求重构代码(提取组件、合并重复逻辑、重命名变量、优化结构)时触发。提供重构安全检查清单,防止丢失原始上下文。
重构安全规范
触发场景
- 用户说"重构"、"提取"、"合并"、"优化结构"、"简化代码"
- 涉及表格列、数据结构、配置项的修改
- 合并条件分支(if/else/switch)
---
重构流程
1. 读取原始代码(必须)
**不要凭记忆或推测重构**,必须完整读取原始代码:
# 读取完整文件
Read: file_path="src/views/Order.vue"
# 如果是条件分支,读取所有分支的代码
Grep: pattern="if.*健康云" -A 50 -B 5
Grep: pattern="else" -A 50 -B 5
**常见错误**:
- ❌ 只读一个分支,推测其他分支
- ❌ 凭记忆重构表格列
- ❌ 假设两个条件分支结构相似
---
2. 制作对比清单
表格重构示例
**原始代码(健康云租户)**:
列1: 订单编号
列2: 订单日期
列3: 材料名称
列4: 材料数量
列5: 单价
列6: 金额
**原始代码(非健康云租户)**:
列1: 订单编号
列2: 创建时间
列3: 材料名称
列4: 单价
列5: 金额
**对比清单**:
| 健康云 | 非健康云 | 状态 | |--------|---------|------| | 订单编号 | 订单编号 | ✅ 一致 | | 订单日期 | 创建时间 | ⚠️ 名称不同 | | 材料名称 | 材料名称 | ✅ 一致 | | 材料数量 | - | ❌ 非健康云无此列 | | 单价 | 单价 | ✅ 一致 | | 金额 | 金额 | ✅ 一致 |
数据结构重构示例
**原始接口**:
interface Order {
id: string
date: string
items: Item[]
total: number
}**重构后接口**:
interface Order {
id: string
createdAt: string // 重命名:date → createdAt
items: Item[]
total: number
}**对比清单**:
- ✅ 字段数量一致(4 个)
- ⚠️ 字段重命名:date → createdAt
- ✅ 字段顺序一致
- ✅ 字段类型一致
---
3. 验证检查点
重构完成后,必须逐项检查:
表格重构检查
- [ ] 列数一致(原始 6 列 → 重构后 6 列)
- [ ] 列顺序一致(第 1 列是订单编号,第 2 列是日期...)
- [ ] 列名一致(或有明确映射关系)
- [ ] 没有遗漏列
- [ ] 没有多余列
条件分支检查
- [ ] 所有 if/else/switch 分支都已处理
- [ ] 每个分支的逻辑与原始代码一致
- [ ] 没有假设某个分支与另一个分支相似
数据结构检查
- [ ] 字段数量一致
- [ ] 字段类型一致
- [ ] 字段顺序一致(如果顺序重要)
- [ ] 没有遗漏字段
---
4. 输出格式
重构完成后,输出对比清单:
## 重构对比
### 原始结构(健康云租户)
- 列1: 订单编号
- 列2: 订单日期
- 列3: 材料名称
- 列4: 材料数量
- 列5: 单价
- 列6: 金额
### 原始结构(非健康云租户)
- 列1: 订单编号
- 列2: 创建时间
- 列3: 材料名称
- 列4: 单价
- 列5: 金额
### 重构后结构
- 列1: 订单编号
- 列2: 日期(健康云显示"订单日期",非健康云显示"创建时间")
- 列3: 材料名称
- 列4: 材料数量(仅健康云显示)
- 列5: 单价
- 列6: 金额
### 变更说明
- 合并:订单日期 + 创建时间 → 日期(条件显示)
- 条件显示:材料数量仅在健康云租户显示
- 删除:无
- 新增:无
---
常见陷阱
1. 假设驱动重构
**错误示例**:
// 只读了健康云租户的代码
if (isHealthCloud) {
columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
}
// ❌ 假设非健康云租户只是列名不同
else {
columns = ['订单编号', '创建时间', '材料名称', '材料数量', '单价', '金额']
}**正确做法**:
// 读取两个分支的原始代码
if (isHealthCloud) {
columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
} else {
// 非健康云租户没有"材料数量"列
columns = ['订单编号', '创建时间', '材料名称', '单价', '金额']
}2. 记忆重构
**错误示例**:
// ❌ 凭记忆重构,没有读取原始代码
const columns = ['订单编号', '材料名称', '单价', '金额', '订单日期']
**正确做法**:
// ✅ 读取原始代码,确认列顺序
Read: file_path="src/views/Order.vue"
// 原始代码:['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
const columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
3. 跳过验证
**错误示例**:
// 重构完成,直接提交
// ❌ 没有验证列数、列顺序、列名
**正确做法**:
## 验证清单
- [x] 列数一致:原始 6 列 → 重构后 6 列
- [x] 列顺序一致:订单编号、订单日期、材料名称、材料数量、单价、金额
- [x] 列名一致:完全一致
- [x] 没有遗漏列
- [x] 没有多余列
5. 提取子模块导致循环依赖
**场景**:从大服务/组件拆分子模块时,子模块需要回调父模块的方法
⚠️ 预检流程(拆分前必须执行)
**先提取共享方法,再拆分子服务**:
拆分前:
1. 扫描父服务,识别被多个子服务调用的工具方法
2. 将这些方法提取到独立 Helper/Utils 类
3. 然后再拆分子服务(此时子服务依赖 Helper,不依赖父服务)
❌ 错误顺序:拆分子服务 → 发现循环依赖 → 修复
✅ 正确顺序:提取共享方法 → 拆分子服务 → 无循环依赖
⚠️ 重复模式警告
**如果第一个子服务提取时已出现循环依赖,后续所有子服务提取必须先检查相同问题**:
真实案例:
- ReportService → 提取 ReportInvoiceService → 循环依赖(resolveTenantIds)
- ReportService → 提取 ReportPaymentService → 同样的循环依赖!
根因:resolveTenantIds() 留在 ReportService,所有子服务都需要它
修复:应在第一次发现时就提取 TenantHelper,避免后续重复犯错
**错误示例**:
// ❌ ReportService 拆分出 ReportInvoiceService
// 但 ReportInvoiceService 又需要调用 ReportService.resolveTenantIds()
@Service
@RequiredArgsConstructor
public class ReportInvoiceService {
private final ReportService reportService; // 循环依赖!
}**正确做法(按优先级)**:
// ✅ 方案1:提取公共方法到独立工具类(推荐)
@Component
public class TenantHelper {
public List<Long> resolveTenantIds(Long tenantId) { ... }
}
// ✅ 方案2:@Lazy 字段注入(应急)
@Service
public class ReportInvoiceService {
@Autowired @Lazy
private ReportService reportService;
}
// ✅ 方案3:函数式回调
public void process(Function<Long, List<Long>> tenantResolver) { ... }**检查清单**:
- [ ] **预检**:父服务中是否有被多个子服务调用的工具方法?是 → 先提取到独立类
- [ ] **重复检查**:本次拆分是否与之前的拆分有相同的回调依赖?
- [ ] 画依赖图:拆分后是否存在 A → B → A 的循环?
- [ ] 依赖方向是否单向:父 → 子(禁止子 → 父)
- [ ] Spring Boot 3.x 默认禁止构造器循环依赖
**不仅限于 Spring**:
- Go:包级循环引用(编译错误)→ 提取公共包
- Vue:组件循环引用 → 异步组件或提取公共逻辑到 composables
- TypeScript:模块循环引用 → 提取公共模块
---
6. 提取状态管理时的初始化冲突
**场景**:将组件内的 state 提取到自定义 Hook/composable/Service 时,封装的 open 方法添加了初始化逻辑,覆盖了调用方预设的状态
**真实案例**:
// 重构前:调用方直接控制状态,时序正确
setMarkPaidText(selectedOrderNumbers) // 1. 设置订单号
setMarkPaidModalVisible(true) // 2. 打开弹窗 ✅
// 重构后:提取到 useOrderModals hook
const openMarkPaidModal = () => {
setMarkPaidText('') // ❌ 清空了调用方刚设置的值!
setMarkPaidModalVisible(true)
}
// 调用方:
setMarkPaidText(selectedOrderNumbers) // 1. 设置订单号
openMarkPaidModal() // 2. 内部又清空了 → 弹窗空白**正确做法**:
// ✅ 方案1:open 方法接受参数
const openMarkPaidModal = (initialText?: string) => {
if (initialText !== undefined) setMarkPaidText(initialText)
setMarkPaidModalVisible(true)
}
// ✅ 方案2:初始化放在 close 而非 open
const closeMarkPaidModal = () => {
setMarkPaidText('') // 关闭时清空是安全的
setMarkPaidModalVisible(false)
}**检查清单**:
- [ ] 提取 open/show 方法时,检查调用方是否在 open 之前设置了状态
- [ ] open 方法中的初始化逻辑是否会覆盖调用方预设的值
- [ ] 初始化/清空逻辑应放在 close 而非 open
- [ ] 如果 open 需要初始化,应通过参数传入而非内部硬编码
**适用范围**:
- React:useState → useXxxModal hook
- Vue:ref → useXxxDialog composable
- Java:Service 方法添加默认值逻辑
---
7. UI 组件重构时的布局结构丢失
**场景**:重构 Modal/Dialog/Drawer 等容器组件时,将复杂的 title/header/footer 简化,导致按钮布局和功能丢失
**真实案例**:
// 重构前:title 包含条件渲染的按钮
<Modal
title={
<div className="flex justify-between">
<span>发票详情</span>
{!editMode && <Button icon={<EditOutlined />}>编辑发票</Button>}
{editMode && (
<Space>
<Button icon={<CloseOutlined />}>取消</Button>
<Button type="primary" icon={<SaveOutlined />}>保存</Button>
</Space>
)}
</div>
}
footer={[
<Button key="downloaRead more
name: refactor-safety description: 当用户要求重构代码(提取组件、合并重复逻辑、重命名变量、优化结构)时触发。提供重构安全检查清单,防止丢失原始上下文。
重构安全规范
触发场景
- 用户说"重构"、"提取"、"合并"、"优化结构"、"简化代码"
- 涉及表格列、数据结构、配置项的修改
- 合并条件分支(if/else/switch)
---
重构流程
1. 读取原始代码(必须)
**不要凭记忆或推测重构**,必须完整读取原始代码:
# 读取完整文件 Read: file_path="src/views/Order.vue" # 如果是条件分支,读取所有分支的代码 Grep: pattern="if.*健康云" -A 50 -B 5 Grep: pattern="else" -A 50 -B 5
**常见错误**:
- ❌ 只读一个分支,推测其他分支
- ❌ 凭记忆重构表格列
- ❌ 假设两个条件分支结构相似
---
2. 制作对比清单
表格重构示例
**原始代码(健康云租户)**:
列1: 订单编号 列2: 订单日期 列3: 材料名称 列4: 材料数量 列5: 单价 列6: 金额
**原始代码(非健康云租户)**:
列1: 订单编号 列2: 创建时间 列3: 材料名称 列4: 单价 列5: 金额
**对比清单**:
| 健康云 | 非健康云 | 状态 | |--------|---------|------| | 订单编号 | 订单编号 | ✅ 一致 | | 订单日期 | 创建时间 | ⚠️ 名称不同 | | 材料名称 | 材料名称 | ✅ 一致 | | 材料数量 | - | ❌ 非健康云无此列 | | 单价 | 单价 | ✅ 一致 | | 金额 | 金额 | ✅ 一致 |
数据结构重构示例
**原始接口**:
interface Order {
id: string
date: string
items: Item[]
total: number
}**重构后接口**:
interface Order {
id: string
createdAt: string // 重命名:date → createdAt
items: Item[]
total: number
}**对比清单**:
- ✅ 字段数量一致(4 个)
- ⚠️ 字段重命名:date → createdAt
- ✅ 字段顺序一致
- ✅ 字段类型一致
---
3. 验证检查点
重构完成后,必须逐项检查:
表格重构检查
- [ ] 列数一致(原始 6 列 → 重构后 6 列)
- [ ] 列顺序一致(第 1 列是订单编号,第 2 列是日期...)
- [ ] 列名一致(或有明确映射关系)
- [ ] 没有遗漏列
- [ ] 没有多余列
条件分支检查
- [ ] 所有 if/else/switch 分支都已处理
- [ ] 每个分支的逻辑与原始代码一致
- [ ] 没有假设某个分支与另一个分支相似
数据结构检查
- [ ] 字段数量一致
- [ ] 字段类型一致
- [ ] 字段顺序一致(如果顺序重要)
- [ ] 没有遗漏字段
---
4. 输出格式
重构完成后,输出对比清单:
## 重构对比 ### 原始结构(健康云租户) - 列1: 订单编号 - 列2: 订单日期 - 列3: 材料名称 - 列4: 材料数量 - 列5: 单价 - 列6: 金额 ### 原始结构(非健康云租户) - 列1: 订单编号 - 列2: 创建时间 - 列3: 材料名称 - 列4: 单价 - 列5: 金额 ### 重构后结构 - 列1: 订单编号 - 列2: 日期(健康云显示"订单日期",非健康云显示"创建时间") - 列3: 材料名称 - 列4: 材料数量(仅健康云显示) - 列5: 单价 - 列6: 金额 ### 变更说明 - 合并:订单日期 + 创建时间 → 日期(条件显示) - 条件显示:材料数量仅在健康云租户显示 - 删除:无 - 新增:无
---
常见陷阱
1. 假设驱动重构
**错误示例**:
// 只读了健康云租户的代码
if (isHealthCloud) {
columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
}
// ❌ 假设非健康云租户只是列名不同
else {
columns = ['订单编号', '创建时间', '材料名称', '材料数量', '单价', '金额']
}**正确做法**:
// 读取两个分支的原始代码
if (isHealthCloud) {
columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
} else {
// 非健康云租户没有"材料数量"列
columns = ['订单编号', '创建时间', '材料名称', '单价', '金额']
}2. 记忆重构
**错误示例**:
// ❌ 凭记忆重构,没有读取原始代码 const columns = ['订单编号', '材料名称', '单价', '金额', '订单日期']
**正确做法**:
// ✅ 读取原始代码,确认列顺序 Read: file_path="src/views/Order.vue" // 原始代码:['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额'] const columns = ['订单编号', '订单日期', '材料名称', '材料数量', '单价', '金额']
3. 跳过验证
**错误示例**:
// 重构完成,直接提交 // ❌ 没有验证列数、列顺序、列名
**正确做法**:
## 验证清单 - [x] 列数一致:原始 6 列 → 重构后 6 列 - [x] 列顺序一致:订单编号、订单日期、材料名称、材料数量、单价、金额 - [x] 列名一致:完全一致 - [x] 没有遗漏列 - [x] 没有多余列
5. 提取子模块导致循环依赖
**场景**:从大服务/组件拆分子模块时,子模块需要回调父模块的方法
⚠️ 预检流程(拆分前必须执行)
**先提取共享方法,再拆分子服务**:
拆分前: 1. 扫描父服务,识别被多个子服务调用的工具方法 2. 将这些方法提取到独立 Helper/Utils 类 3. 然后再拆分子服务(此时子服务依赖 Helper,不依赖父服务) ❌ 错误顺序:拆分子服务 → 发现循环依赖 → 修复 ✅ 正确顺序:提取共享方法 → 拆分子服务 → 无循环依赖
⚠️ 重复模式警告
**如果第一个子服务提取时已出现循环依赖,后续所有子服务提取必须先检查相同问题**:
真实案例: - ReportService → 提取 ReportInvoiceService → 循环依赖(resolveTenantIds) - ReportService → 提取 ReportPaymentService → 同样的循环依赖! 根因:resolveTenantIds() 留在 ReportService,所有子服务都需要它 修复:应在第一次发现时就提取 TenantHelper,避免后续重复犯错
**错误示例**:
// ❌ ReportService 拆分出 ReportInvoiceService
// 但 ReportInvoiceService 又需要调用 ReportService.resolveTenantIds()
@Service
@RequiredArgsConstructor
public class ReportInvoiceService {
private final ReportService reportService; // 循环依赖!
}**正确做法(按优先级)**:
// ✅ 方案1:提取公共方法到独立工具类(推荐)
@Component
public class TenantHelper {
public List<Long> resolveTenantIds(Long tenantId) { ... }
}
// ✅ 方案2:@Lazy 字段注入(应急)
@Service
public class ReportInvoiceService {
@Autowired @Lazy
private ReportService reportService;
}
// ✅ 方案3:函数式回调
public void process(Function<Long, List<Long>> tenantResolver) { ... }**检查清单**:
- [ ] **预检**:父服务中是否有被多个子服务调用的工具方法?是 → 先提取到独立类
- [ ] **重复检查**:本次拆分是否与之前的拆分有相同的回调依赖?
- [ ] 画依赖图:拆分后是否存在 A → B → A 的循环?
- [ ] 依赖方向是否单向:父 → 子(禁止子 → 父)
- [ ] Spring Boot 3.x 默认禁止构造器循环依赖
**不仅限于 Spring**:
- Go:包级循环引用(编译错误)→ 提取公共包
- Vue:组件循环引用 → 异步组件或提取公共逻辑到 composables
- TypeScript:模块循环引用 → 提取公共模块
---
6. 提取状态管理时的初始化冲突
**场景**:将组件内的 state 提取到自定义 Hook/composable/Service 时,封装的 open 方法添加了初始化逻辑,覆盖了调用方预设的状态
**真实案例**:
// 重构前:调用方直接控制状态,时序正确
setMarkPaidText(selectedOrderNumbers) // 1. 设置订单号
setMarkPaidModalVisible(true) // 2. 打开弹窗 ✅
// 重构后:提取到 useOrderModals hook
const openMarkPaidModal = () => {
setMarkPaidText('') // ❌ 清空了调用方刚设置的值!
setMarkPaidModalVisible(true)
}
// 调用方:
setMarkPaidText(selectedOrderNumbers) // 1. 设置订单号
openMarkPaidModal() // 2. 内部又清空了 → 弹窗空白**正确做法**:
// ✅ 方案1:open 方法接受参数
const openMarkPaidModal = (initialText?: string) => {
if (initialText !== undefined) setMarkPaidText(initialText)
setMarkPaidModalVisible(true)
}
// ✅ 方案2:初始化放在 close 而非 open
const closeMarkPaidModal = () => {
setMarkPaidText('') // 关闭时清空是安全的
setMarkPaidModalVisible(false)
}**检查清单**:
- [ ] 提取 open/show 方法时,检查调用方是否在 open 之前设置了状态
- [ ] open 方法中的初始化逻辑是否会覆盖调用方预设的值
- [ ] 初始化/清空逻辑应放在 close 而非 open
- [ ] 如果 open 需要初始化,应通过参数传入而非内部硬编码
**适用范围**:
- React:useState → useXxxModal hook
- Vue:ref → useXxxDialog composable
- Java:Service 方法添加默认值逻辑
---
7. UI 组件重构时的布局结构丢失
**场景**:重构 Modal/Dialog/Drawer 等容器组件时,将复杂的 title/header/footer 简化,导致按钮布局和功能丢失
**真实案例**:
// 重构前:title 包含条件渲染的按钮
<Modal
title={
<div className="flex justify-between">
<span>发票详情</span>
{!editMode && <Button icon={<EditOutlined />}>编辑发票</Button>}
{editMode && (
<Space>
<Button icon={<CloseOutlined />}>取消</Button>
<Button type="primary" icon={<SaveOutlined />}>保存</Button>
</Space>
)}
</div>
}
footer={[
<Button key="downloa保留你熟悉的 CLI/IDE,让 Claude Code、Gemini CLI、Codex、Cursor、GitHub Copilot 开箱即用 按费力度从低到高,用最少操作获得最大帮助 不是提示词集合,而是一套可维护的 AI 协作配置系统。
Repo: doccker/cc-use-exp
Other skills on cc-use-exp.
- /api-design-safety
当设计或修改 REST API 响应结构、处理 API 返回值,或生成 Excel/CSV/PDF/对账文件等下游产物时触发。防止 API 设计缺陷导致的字段错位、类型歧义,以及生成产物时关键字段缺失但静默成功的问题。
Open skill - /api-proxy-safety
网关/代理/WAF/CDN 中间件的安全关键词匹配实现规范,防止纯子串匹配误判正常响应内容中的技术术语(如 Cloudflare、502、error)
Open skill - /async-task-pattern
当 API/任务可能执行超过 10 秒(批量数据处理、远程 API 批量调用、全表扫描、跨租户聚合)时触发。防止同步接口被网关 30s 超时切断、用户重复点击触发并发、状态缓存内存泄漏等问题。提供异步任务状态机标准模板。
Open skill - /bash-style
当用户操作 .sh、Dockerfile、Makefile、.yml、.yaml 文件,或在 Markdown 中编写 bash 代码块时触发。提供 Bash 编写规范。
Open skill - /code-quality-principles
当编写新模块、设计接口、重构代码或代码审查时触发。提供经典模块化六原则检查清单(大小适中/调用深度/扇入扇出/边界清晰/作用域内聚/可预测性),适用于 PR/Review/新模块设计场景。
Open skill - /external-system-debugging
涉及浏览器、编辑器、CDN/WAF、IM 平台、操作系统剪贴板、第三方 SaaS 等"外部黑盒系统"的代码编写或 bug 调试时触发。强制先抓真实环境数据再推理,避免连续 2 轮"凭代码推理"的修复 no-op。关键词:粘贴/复制异常、跨平台显示不一致、第三方 API 怪结果、CDN/WAF 拦截、本地复现失败、HTML→MD 转换丢属性。
Open skill

