Back to skills
SKILL.md
Cleaner
ASecurity代码复杂度体检。用 CRAP 指标(复杂度×覆盖率)定位难维护的烂函数,只报告不改码。先圈定审查范围→归属到项目→判断语言与测试模块→跑 CRAP。当用户提到"CRAP"、"复杂度检查"、"找烂函数"、"代码体检"、"哪些方法该重构"、"cleaner",或刚改完一批代码想评估质量时使用。跨项目通用,不硬编码路径。
- 5 stars
- 0 votes
- 0 copies
- 0 views
- Added November 7, 2026
Security analysis
100/100Pro scans all 3 files and shows the line behind each finding
npx -y skills add Lixiuxiu559/portunus --skill cleaner --agent claude-codeAre you the author of Cleaner?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/lixiuxiu559-cleaner)---
name: cleaner
description: 代码复杂度体检。用 CRAP 指标(复杂度×覆盖率)定位难维护的烂函数,只报告不改码。先圈定审查范围→归属到项目→判断语言与测试模块→跑 CRAP。当用户提到"CRAP"、"复杂度检查"、"找烂函数"、"代码体检"、"哪些方法该重构"、"cleaner",或刚改完一批代码想评估质量时使用。跨项目通用,不硬编码路径。
---
# 代码复杂度体检(CRAP)
你要定位"改起来危险"的函数,产出一份高危清单供后续修复。核心是一条**先界定范围、再精准体检**的流水线,而不是无脑全量扫描。
**只报告,不改码。** 你的产出是清单和修复方向,改由 coder 执行。这条边界让体检和修复解耦:体检要客观,修复要判断,两件事分开做更清醒。
## CRAP 是什么
```
CRAP(m) = comp(m)² × (1 − cov(m))³ + comp(m)
comp = 函数圈复杂度
cov = 函数覆盖率(0~1)
```
复杂度越高、覆盖率越低,CRAP 越高。降 CRAP 只有两条路:**拆分降复杂度** 或 **补测试提覆盖率**。阈值 30(业界默认),超过即建议重构。
为什么用这个而不是单看复杂度:一个复杂但测试充分的函数,改它有测试兜底,没那么危险;一个复杂又没测试的函数,一动就可能出事——CRAP 正是捕捉后者。
## 流水线
体检不是"对着整个仓库跑脚本",而是四步:**定范围 → 归属项目 → 判语言和测试 → 跑 CRAP**。前三步决定了第四步跑得准不准、有没有意义。
### 1. 定范围(看语境,别急着问)
从对话语境推断该体检什么,按线索优先:
- **刚改完代码** → 看本次改动:`git diff --name-only`(未提交)或 `git diff HEAD~1 --name-only`(最近一次提交)。这是最常见场景。
- **用户点了某模块/目录/文件** → 就那个范围。
- **明确说全量 / 首次体检** → 扫整个项目。
- **啥线索都没有** → 简短问一句要体检哪块,或默认取最近改动。别默认全量扫大仓库——噪声多、慢。
范围界定的意义:CRAP 报告只有聚焦才有行动力。全量扫一个老项目会列出几百个历史遗留高危项,淹没本次真正该关注的。
### 2. 归属到项目
一个仓库常含多个子项目(monorepo)。把范围内的文件归到各自的**构建单元**——找最近的构建声明:
- `pom.xml` → Java(Maven)
- `pyproject.toml` / `setup.py` → Python
- `package.json` → JS/TS
同一批改动可能横跨多个项目(如后端 + 前端),分别归类,逐个体检。
### 3. 判断语言与测试模块(能不能算 CRAP)
CRAP 需要"函数级复杂度 + 函数级覆盖率"两份数据。缺覆盖率就算不了——所以先确认每个项目**有没有测试/覆盖率工具链**:
| 语言 | 状态 | 覆盖率 + 复杂度来源 |
|------|:---:|--------|
| **Java** | ✅ 已支持 | JaCoCo `jacoco.xml`(单文件自带 COMPLEXITY + LINE) |
| Python | ⬜ 未实装 | 需 `coverage json`(函数级覆盖) + `radon cc -j`(复杂度),按函数名 join |
| JS/TS | ⬜ 未实装 | 需 istanbul `coverage-final.json` + ESLint complexity/escomplex,按位置 join |
判断逻辑:
- **Java 且有 JaCoCo 报告** → 能跑,进第 4 步
- **Java 但没测试/没覆盖率报告** → 提示先跑 `mvn test`(JaCoCo report 绑定 test phase,自动产出);没有测试则如实说"该项目无覆盖率数据,CRAP 只能退化为纯复杂度排序"
- **Python / JS 未实装** → 如实告知上表所列的前置工具,**不硬凑、不跑空**。`scripts/crap.js` 的 parser 注册表已预留扩展位,基建就绪后加一个 parser 即可
**诚实优先**:宁可说"这个项目现在算不了 CRAP,需要先配 X",也不要假装能跑、给出误导数字。
### 4. 跑 CRAP(委托默认子 agent)
跑 CRAP 会碰大文件(jacoco.xml 可达数 MB)、可能触发 `mvn test`(输出冗长),这些会污染主上下文。所以**把执行委托给一个默认子 agent**,让它在隔离上下文里跑脚本、只回传精炼结果。
给子 agent 的指令要点:
1. 定位 JaCoCo 报告(不写死路径):
`find . -path '*/target/site/jacoco/jacoco.xml' -not -path '*/node_modules/*'`
缺失则先 `mvn test` 生成(耗时长,可后台跑)
2. 跑体检脚本(纯 Node,零依赖,无需 install):
`node <本skill目录>/scripts/crap.js <jacoco.xml路径> --lang java --threshold 30 --top 30`
- 脚本在本 skill 的 `scripts/crap.js`;不传 `--lang` 会按文件名自动探测
- 超阈值方法存在时退出码为 1
3. 回传:高危方法清单(CRAP 值 / 复杂度 / 覆盖率 / 方法全名),不要回传原始报告或 mvn 日志
若范围能进一步缩小(只体检本次改动的类),让子 agent 在脚本输出里按包名/类名过滤,只留范围内的。
## 出报告
呈现高危方法清单,并对 Top 高危项给出**修复方向**:
- 复杂度高(如 >20)且覆盖率低 → 优先**拆分方法**(光补测试压不下来 CRAP)
- 复杂度中等但覆盖率 0 → 优先**补测试**
- 区分"真高危"(复杂度本身就大)和"覆盖率虚低"(可能只是这次没跑到测试,而非没测试)
**覆盖率全 0 的情形**:说明这次 mvn test 没跑到业务代码(可能只跑了少量单测)。此时 CRAP ≈ 复杂度²+复杂度,本质在排**圈复杂度最高**的方法——仍有价值,但报告里要说明这个前提,别让人误以为"完全没测试"。
## 移交
把清单和修复方向交给主流程 / 用户。**不要自己改代码**——由 coder 按报告修复(拆函数或补测试)。
指标是手段不是目的:不要为压 CRAP 值而建议无意义的过度拆分,也不要为提覆盖率而写空断言(那是 reinforcer 该抓的)。
## 与其他质量工具的边界
只管**复杂度**这一个维度。测试有效性(突变测试)交给 reinforcer,行为验收交给 qa,通用质量/规范交给 code-review。四者不重叠,各管一段。
Files in this skill
- SKILL.md
- evals/evals.json
- scripts/crap.js
Attribution
Comments
Loading comments…
Newsletter
Get the weekly list of safe new skills, and the ones we pulled.