关于 omen
Omen 是多语言代码分析工具,用于识别复杂度热点、技术债、变更频繁的文件和缺陷风险。它面向希望在 AI 助手编写代码时掌握潜在风险的开发者和团队。
主要功能
- 01圈复杂度与认知复杂度分析
- 02自承认技术债(SATD)注释分类
- 03占位实现(stub)检测与 CI 门禁
- 04基于 git 历史的变更热点分析
- 05文件与提交级别的缺陷风险评估
适用场景
- 01在合并 PR 前识别需要额外审查的提交
- 02在 CI 中阻止遗留 todo!() 等占位实现
- 03排查高复杂度且频繁修改的文件
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
docker run -i --rm ghcr.io/panbanda/omen:3.0.0本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。
ghcr.io/panbanda/omen:3.0.0stdio工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
Omen
调用图报告会公开 call_resolutions(符号)和针对整个输入的
resolution_summary(影响)。这些字段用于区分唯一名称候选项、存在歧义的调用和未解析的调用。唯一候选项并非经编译器验证的绑定;被排除的边也不能证明某项编辑是安全的。有关限制和回归门槛,请参阅
改进标准和
实测结果。
你的 AI 会编写代码,却不知道哪里暗藏陷阱。
Omen 为 AI 助手提供所需的上下文:复杂度热点、隐藏依赖项、容易引发缺陷的文件,以及开发者自己承认的技术债务。一条命令即可揭示那些看不见的问题。
为什么叫“Omen”? Omen(征兆)预示着未来可能发生的好事或坏事。你的代码库中遍布征兆:低复杂度和整洁的架构预示着前路顺畅;频繁变更、技术债务和代码克隆则警示着麻烦正在酝酿。Omen 会揭示这些信号,让你能在那个“临时修复方案”迎来上线三周年之前采取行动。
功能
复杂度分析 - 代码的理解和测试难度
复杂度分为两种:
-
圈复杂度统计代码中不同执行路径的数量。每个
if、for、while或switch都会创建一条新路径。圈复杂度为 10 的函数意味着有 10 种不同的执行方式。数值越高,需要覆盖所有场景的测试用例就越多。 -
认知复杂度衡量代码对人类而言有多难读。它对深层嵌套的代码(例如
for中的if,再嵌套在另一个if中)比结构扁平的代码惩罚更重。两个函数的圈复杂度可能相同,但嵌套更深的函数认知复杂度会更高,因为更难理清代码的执行过程。
**为何重要:**研究表明,复杂代码更容易出现 Bug,也需要更长时间修复。McCabe 于 1976 年发表的原始论文发现,复杂度超过 10 的函数维护起来要困难得多。SonarSource 的认知复杂度则在此基础上进一步衡量那些真正让开发者困惑的因素。
提示
将每个函数的圈复杂度控制在 10 以下,认知复杂度控制在 15 以下。
开发者自认技术债务(SATD) - 开发者承认走了捷径的注释
开发者写下 TODO: fix this later 或 HACK: this is terrible but works 时,就产生了技术债务,也承认了这一点。Omen 会查找这些注释并按类型分组:
重要原因:Potdar and Shihab 2014 年的研究发现,SATD 注释往往会在代码库中保留数年。保留时间越长,就越难修复,因为人们会忘记上下文。Maldonado and Shihab(2015)指出,设计债务是最常见、也最危险的类型。
提示
每周检查 SATD。如果某个 TODO 已存在超过 6 个月,就修复或删除它。
存根检测 - 未完成的实现或占位实现
SATD 是开发者选择保留的债务;存根则是从未完成的工作——也就是 Agent 或人类在任务中途留下的内容。Omen 通过 tree-sitter AST 分析查找存根(绝不进行纯字符串匹配,因此包含触发词的字符串字面量绝不会被误报),并按模式类型分组:
| 类别 | 严重性 | 含义 |
|---|---|---|
not_implemented | 高 | 明确表示未实现的惯用写法:todo!()、raise NotImplementedError、throw new Error('not implemented') |
elision | 中 | 承认跳过工作的注释:// ... rest of the implementation、// placeholder、// your code here |
empty_body | 中 | 函数/方法的函数体为空,且不应合理地为空(返回类型非 void,或内部含有省略注释) |
omen stubs
# Fail CI if any stub is found
omen stubs --gate error
# Only fail on high-severity stubs (todo!()-style idioms), warn on the rest
omen stubs --gate error --gate-severity high
omen stubs --gate warn
**重要原因:**看起来完整的未完成工作(可以编译,也已提交)比明显缺失的代码更危险——在它于生产环境中被调用之前,没有任何迹象表明需要处理。存根与 SATD 一样,会计入综合健康评分中的债务部分,因此未解决的存根会像未解决的 TODO 一样拉低代码库评分。
提示
在 CI 中添加
omen stubs --gate error,以阻止留下todo!()或NotImplementedError的合并。
死代码检测 - 存在但从未运行的代码
死代码包括:
- 从未调用的函数
- 已赋值但从未使用的变量
- 从未实例化的类
return语句之后永远无法执行的代码
**重要原因:**死代码不只是杂乱无用。它会让新开发者误以为它必定很重要。它会增加构建时间和二进制文件大小。最糟糕的是,它可能掩盖 bug——如果有人以为死代码会运行而去“修复”它,就白费了时间。Romano et al.(2020)发现,死代码是其他代码质量问题的有力预测指标。
提示
删除死代码。借助版本控制,需要时随时可以恢复。
Git 变更频率分析 - 文件随时间变化的频率
变更频率分析会检查你的 git 历史记录并统计:
-
每个文件被修改的次数
-
添加和删除的行数
-
一起发生变化的文件 高频变更的文件称为“热点”——它们一直在被修改,这可能意味着它们:
-
是系统的核心部分(所有人都需要修改它们)
-
设计不佳(需要不断修复缺陷)
-
缺少良好的抽象(功能不断被添加进去)
为什么重要:Nagappan 和 Ball 于 2005 年在 Microsoft 开展的研究发现,代码变更量是最有力的缺陷预测指标之一。频繁变更的文件往往包含更多缺陷。结合复杂度数据,变更量可以帮助你找出既复杂又经常修改的文件——也就是风险最高的代码。
提示
如果文件的变更量高且复杂度高,应优先对其进行重构。
代码克隆检测 - 在多个位置出现的重复代码
克隆分为三种类型:
| 类型 | 描述 | 示例 |
|---|---|---|
| Type-1 | 完全相同的副本(空格或注释可能不同) | 复制粘贴的代码 |
| Type-2 | 结构相同,名称不同 | 重命名变量的同一函数 |
| Type-3 | 相似代码,但经过了一些修改 | 几乎执行相同操作的函数 |
**为什么重要:**在一份副本中修复缺陷后,还得记得修复所有其他副本。Juergens 等人(2009)发现,克隆代码的缺陷明显更多,因为修复无法一致地应用。克隆越多,在更新时漏掉某一份的可能性就越大。
提示
任何复制超过两次的代码都应该考虑提取为共享函数。目标是将重复率控制在 5% 以下。
缺陷预测 - 文件包含缺陷的可能性
Omen 使用 PMAT 加权指标,结合多种信号来预测缺陷概率:
- 过程指标(变更频率、所有权分散程度)
- 度量指标(圈复杂度/认知复杂度)
- 年龄(代码年龄和稳定性)
- 总计大小(代码行数)
每个文件都会获得一个 0% 到 100% 的风险评分。
**为什么重要:**你无法对所有内容进行同等程度的审查。Menzies 等人(2007)表明,缺陷预测可以帮助团队将测试和代码审查重点放在最有可能存在问题的文件上。Rahman 等人(2014)发现,即使是简单模型,在查找缺陷方面也优于随机选择文件。
提示
应优先审查缺陷概率 >70% 的文件。
变更风险分析(JIT) - 预测哪些提交可能引入缺陷
即时(JIT)缺陷预测会分析近期提交,在风险变更造成问题之前识别出来。与文件级预测不同,JIT 在提交级别运行,使用 Kamei 等人(2013)提出的变更范围因素,并结合软件工程研究文献中的文件级风险信号。
变更范围因素(占评分的 75%):
使用 Kamei 的中位数逻辑回归系数进行加权(跨项目的相对排序):
| 因素 | 名称 | 权重 | 衡量内容 |
|---|---|---|---|
| LA | 新增行数 | 0.16 | 新增越多 = 风险越高(最强预测指标) |
| ENTROPY | 变更熵 | 0.14 | 变更越分散 = 越难审查 |
| FIX | Bug 修复 | 0.12 | Bug 修复提交表明存在问题区域 |
| LD | 删除行数 | 0.08 | 删除通常更安全 |
| NF | 文件数量 | 0.08 | 文件越多 = 协作风险越高 |
| NUC | 独特变更 | 0.07 | 文件中独特的历史提交越多 |
| NDEV | 开发者数量 | 0.05 | 参与文件的开发者越多 = 风险越高 |
| EXP | 开发者经验 | 0.05 | 经验越少 = 风险越高 |
文件级风险信号(占评分的 25%):
| 信号 | 权重 | 来源 |
|---|---|---|
| 文件变更频率 | 0.10 | Nagappan & Ball (2005) - 历史变更频率可预测缺陷 |
| 文件复杂度 | 0.08 | Zimmermann & Nagappan (2008) - 圈复杂度具有预测能力,但弱于变更指标 |
| 代码所有权分散度 | 0.07 | Bird et al. (2011) - 有许多少量贡献者(没有明确负责人)的文件存在更多缺陷 |
代码所有权分散度的计算方式为 1 - max_author_percentage:没有任何一位作者占主导地位的文件,风险评分会更高。这符合 Bird 等人的研究发现:集中的代码所有权(明确的主要作者)与较少缺陷相关,而分散的代码所有权则与更多缺陷相关。
基于百分位数的风险分类:
风险级别采用基于百分位数的阈值,遵循 JIT 缺陷预测最佳实践。提交不会使用固定阈值,而是根据代码库自身的分布进行排序:
| 级别 | 百分位数 | 含义 |
|---|---|---|
| 高 | 前 5% | P95+ - 值得额外仔细审查 |
| 中 | 前 20% | P80-P95 - 值得额外关注 |
| 低 | 后 80% | 低于 P80 - 标准审查流程 |
这种方法符合缺陷预测研究中的 80/20 法则:约 20% 的代码变更包含约 80% 的缺陷。无论代码库的特征如何,它都能确保结果具有可操作性——规范管理的代码库阈值较低,而变更频繁的代码库阈值较高。
重要性: Kamei et al. (2013) 证明,JIT 预测能够在提交时发现高风险变更,避免 Bug 扩散。他们考虑工作量的方案采用排序而非固定阈值,将有限的审查资源集中在风险最高的约 20% 提交上。Zeng et al. (2021) 表明,简单的 JIT 模型准确率可与深度学习相当(约 65%),且更易解释。
提示
合并 PR 前运行
omen changes,找出需要额外审查的提交。
PR/分支差异风险分析 - 在合并前评估分支的整体风险
JIT 分析会检查单个提交,而差异分析则会评估整个分支相对于目标分支的累计变更。这让审查者在深入代码审查前,能够快速评估风险。
用法:
# Compare current branch against main
omen diff --target main
# Compare against a specific commit
omen diff --target abc123
# Output as markdown for PR comments
omen -f markdown diff --target main
风险因素:
差异分析器使用与 omen changes 相同的、基于研究的权重模型(见上文),结合变更范围因素与文件级信号:
| 因素 | 衡量内容 | 类别 |
|---|---|---|
| 新增行数 | 引入的新代码总量 | 变更范围 |
| 删除行数 | 移除的代码 | 变更范围 |
| 修改文件数 | 变更的分布范围 | 变更范围 |
| 提交数 | 分支中的提交数量 | 变更范围 |
| 熵 | 变更的分散程度 | 变更范围 |
| 文件变更频率 | 被修改文件的历史变更频率 | 文件级 |
| 文件复杂度 | 被修改文件的最大圈复杂度 | 文件级 |
| 所有权分散度 | 被修改文件的所有权分散程度 | 文件级 |
风险评分解读:
| 分数 | 级别 | 建议操作 |
|---|---|---|
| < 0.2 | 低 | 标准审查流程 |
| 0.2 - 0.5 | 中 | 仔细审查,考虑增加测试 |
| > 0.5 | 高 | 全面审查,确保测试覆盖充分 |
输出示例:
Branch Diff Risk Analysis
==========================
Source: feature/new-api
Target: main
Base: abc123def
Risk Score: 0.31 (MEDIUM)
Changes:
Lines Added: 530
Lines Deleted: 39
Files Modified: 3
Commits: 1
Risk Factors:
entropy: 0.023
lines_added: 0.140
lines_deleted: 0.008
num_files: 0.014
commits: 0.007
file_churn: 0.080
file_complexity: 0.045
ownership_diffusion: 0.000
File Risk:
max_complexity: 6.78
max_churn: 1.00
ownership_diffusion: 0.00
注意事项:
- 新增行数多、删除行数少 - 新功能,需要全面审查
- 新增和删除数量均衡 - 重构,验证行为未发生变化
- 代码净减少 - 清理/简化,通常是积极变化
- 熵高 - 变更分散,检查是否包含无关修改
- 文件数量多 - 影响范围广,确保进行集成测试
- file_churn 高 - 修改了历史上经常变动的不稳定文件
- ownership_diffusion 高 - 被修改的文件没有明确负责人;确保有人负责审查
CI/CD 集成:
# Add to GitHub Actions workflow
- name: PR Risk Assessment
run: |
omen -f markdown diff --target ${{ github.base_ref }} >> $GITHUB_STEP_SUMMARY
重要性: 代码审查时间有限。差异分析有助于审查者优先分配注意力——仅修改 10 行的低风险 PR,相比涉及 17 个文件的中风险 PR,需要的审查力度更小。熵指标对于发现捆绑无关变更的 PR 尤其有用,因为这类 PR 更难审查,也更容易引入 Bug。
提示
创建 PR 前运行
omen diff,了解审查者会如何看待你的变更。考虑将高风险 PR 拆分为更小、更聚焦的变更。
技术债梯度(TDG) - 每个文件的综合“健康评分”
TDG 将多个指标合并为一个分数(范围为 0–100,越高越好):
| 组成部分 | 最高分 | 衡量内容 |
|---|---|---|
| 结构复杂度 | 20 | 圈复杂度和嵌套深度 |
| 语义复杂度 | 15 | 认知复杂度 |
| 重复代码 | 15 | 克隆代码的数量 |
| 耦合度 | 15 | 对其他模块的依赖 |
| 热点 | 10 | 变更频率与复杂度的交互 |
| 时间耦合 | 10 | 与其他文件共同变更的模式 |
| 一致性 | 10 | 对代码风格和模式的遵循程度 |
| 熵 | 10 | 模式熵和代码统一性 |
| 文档 | 5 | 注释覆盖率 |
重要性: 技术债就像金融债务——少量尚可,过多则会造成严重问题。Cunningham 于 1992 年提出了这一术语,而 Kruchten 等人(2012)则对如何衡量和管理技术债进行了规范化。TDG 提供一个单一数值,供你跟踪其随时间的变化并比较不同文件。
提示
在添加新功能前,修复评分低于 70 的文件。跟踪 TDG 平均值的变化趋势——它应该上升,而不是下降。
依赖关系图 - 模块之间的连接方式
Omen 会构建一张图,展示哪些文件导入了哪些其他文件,然后计算:
- PageRank:哪些文件最“核心”(有许多内容依赖它们)
- 介数中心性:哪些文件是代码库不同部分之间的“桥梁”
- 耦合度:模块之间的相互关联程度
**重要性:**高耦合代码很脆弱——修改一个文件可能会导致许多其他文件出问题。Parnas 于 1972 年发表的模块化论文指出,良好的软件设计会尽量减少模块之间的依赖关系。依赖关系图能显示架构中哪些部分清晰,哪些部分错综复杂。
提示
PageRank 高的文件应当特别稳定并经过充分测试。可以考虑拆分那些到处都充当“桥梁”的文件。
热点分析 - 复杂度高且经常变更的高风险文件
热点是指既复杂又经常修改的文件。简单的文件经常变更,通常没什么问题——它很容易处理。复杂的文件很少变更,也可以应对——你可以不去碰它。但复杂的文件不断变更?那就是 Bug 容易滋生的地方。
Omen 使用变更频率和复杂度归一化值的几何平均数计算热点分数:
hotspot = sqrt(churn_percentile * complexity_percentile)
这两个因素都使用经验累积分布函数(CDF)对照行业基准进行归一化,因此分数可在不同项目之间进行比较:
- 变更频率百分位 - 此文件的提交次数在典型开源软件项目中的排名
- 复杂度百分位 - 平均认知复杂度相对于行业基准的排名
| 热点分数 | 严重程度 | 操作 |
|---|---|---|
| >= 0.6 | 严重 | 立即优先处理 |
| >= 0.4 | 高 | 安排审查 |
| >= 0.25 | 中等 | 监控 |
| < 0.25 | 低 | 健康 |
重要性: Adam Tornhill 的《Your Code as a Crime Scene》将热点分析作为寻找影响最大的重构目标的一种方法。他的研究表明,少数文件(通常占 4-8%)包含了大多数 Bug。Graves 等人(2000)和 Nagappan 等人(2005)证明,相对代码变更频率是缺陷的有力预测指标。
提示
从排名前三的热点开始重构。降低高变更频率文件的复杂度,投资回报率最高。
时间耦合 - 一同变更的文件揭示隐藏的依赖关系
如果两个文件总是在同一次提交中变更,它们就存在时间耦合。这通常揭示了:
- 隐藏依赖关系,无法从导入语句中看出
- 逻辑耦合,即一个文件的变更需要另一个文件随之变更
- 意外耦合,由复制粘贴或不一致的抽象导致
Omen 会分析 Git 历史,找出一同变更的文件对:
| 耦合强度 | 含义 |
|---|---|
| > 80% | 几乎总是一起变更——可能存在紧密依赖 |
| 50-80% | 经常耦合——调查两者之间的关系 |
| 20-50% | 中度耦合——可能只是巧合 |
| < 20% | 弱耦合——可能相互独立 |
| 重要性:Ball 等人(1997) 首次在 AT&T 研究了协同变更模式,发现它们能揭示静态分析无法发现的架构违规。Beyer 和 Noack(2005) 指出,时间耦合可以预测未来的变更——如果文件过去曾同时变更,那么它们很可能今后也会再次同时变更。 |
提示
如果两个文件的时间耦合度超过 50%,但不存在导入关系,可以考虑提取一个共享模块,或将它们合并。
代码所有权/公交车因子 - 知识集中程度与团队风险
公交车因子提出的问题是:“在有多少人被公交车撞到后,这段代码会变得无法维护?”公交车因子低,意味着知识集中在少数人手中。
Omen 使用 git blame 计算:
- 主要所有者 - 编写大部分代码的人
- 所有权比例 - 一个人拥有的代码百分比
- 贡献者数量 - 修改过该文件的人数
- 公交车因子 - 主要贡献者人数(贡献代码超过 5%)
| 所有权比例 | 风险等级 | 含义 |
|---|---|---|
| > 90% | 高风险 | 单点故障 |
| 70-90% | 中等风险 | 知识共享有限 |
| 50-70% | 低风险 | 分布合理 |
| < 50% | 非常低 | 所有权分散 |
重要性:Bird 等人(2011) 发现,相比所有权明确的代码,有许多小贡献者参与的代码存在更多缺陷;但由单个人拥有的代码会带来组织风险。最佳状态是每个模块有 2-4 位重要贡献者。Nagappan 等人(2008) 指出,组织指标(如所有权)比单独的代码指标更能预测缺陷。
提示
所有权比例超过 80% 且由单人拥有的文件应做好知识传递文档。关键文件应至少有 2 人了解。
CK 指标 - 面向对象设计质量度量
Chidamber-Kemerer(CK)指标套件用于衡量面向对象设计质量:
| 指标 | 名称 | 衡量内容 | 阈值 |
|---|---|---|---|
| WMC | 每个类的加权方法数 | 方法复杂度之和 | < 20 |
| CBO | 对象间耦合 | 使用的其他类的数量 | < 10 |
| RFC | 类的响应集 | 可调用的方法数 | < 50 |
| LCOM | 方法缺乏内聚性 | 不共享字段的方法数 | < 3 |
| DIT | 继承树深度 | 继承链长度 | < 5 |
| NOC | 子类数量 | 直接子类数 | < 6 |
LCOM(内聚性缺乏) 尤为重要。LCOM 较低意味着类中的方法使用相似的实例变量——该类的职责明确。LCOM 较高意味着该类承担了互不相关的工作,可能应该拆分。
重要性:Chidamber 和 Kemerer 1994 年的论文 奠定了这些指标作为面向对象质量度量基础的地位。Basili 等人(1996) 通过实证验证了这些指标,发现 WMC 和 CBO 与缺陷倾向有很强的相关性。这些指标被引用数千次,至今仍是面向对象设计分析的标准。
代码库映射 - 按 PageRank 排序的符号索引,供大模型使用上下文
代码库映射提供代码库中重要符号的精简摘要,并使用 PageRank 按结构重要性排序。这项功能专为大模型的上下文窗口设计——最重要的函数和类型会优先显示。
对于每个符号,映射包含:
- 名称和类型(函数、类、方法、接口)
- 文件位置和行号
- 用于快速了解的签名
- 基于依赖该符号的其他符号数量计算的 PageRank 分数
- 显示依赖关系的入度/出度
**为何重要:**大模型的上下文窗口有限。将整个文件塞进去,会把令牌浪费在不太重要的代码上。PageRank 由 Brin 和 Page 于 1998 年提出,可识别图中结构上重要的节点。应用于代码时,它能找出对理解代码库最关键的符号。
**可扩展性:**Omen 使用稀疏幂迭代算法计算 PageRank,计算量随边数 O(E) 线性增长,而非随节点数 O(V^2) 二次增长。因此,它可以在 30 秒内快速分析包含 25,000 多个符号的大型单体代码库。
输出示例:
# Repository Map (Top 20 symbols by PageRank)
## parser.ParseFile (function) - pkg/parser/parser.go:45
PageRank: 0.0823 | In: 12 | Out: 5
func ParseFile(path string) (*Result, error)
## models.TdgScore (struct) - pkg/models/tdg.go:28
PageRank: 0.0651 | In: 8 | Out: 3
type TdgScore struct
提示
使用
omen repomap --top 50查看排名最高的 50 个符号,或使用omen context --max-tokens 8000获取更广泛的 Agent 上下文。
功能标志检测 - 在代码库中查找和跟踪功能标志
功能标志功能强大,但也暗藏风险。它们让你可以在不启用代码的情况下发布代码、运行 A/B 测试,并逐步推出功能。但标志会不断累积。2019 年那个“临时”标志至今仍在生产环境中运行。为期一周的实验添加的标志,现在已经成了关键基础设施。
Omen 可检测常见提供商的功能标志用法:
| 提供商 | 语言 | 检测内容 |
|---|---|---|
| LaunchDarkly | JS/TS | variation()、boolVariation() 调用 |
| Split | JS/TS | getTreatment() 调用 |
| Unleash | JS/TS、Python | isEnabled()、is_enabled() 调用 |
| Flipper | Ruby | Flipper[:flag]、enabled?() 调用 |
| 基于 ENV | Ruby、JS/TS、Python | ENV["FEATURE_*"]、process.env.FEATURE_* |
你可以通过在 omen.toml 配置中添加自定义 tree-sitter 查询,来支持其他提供商。
对于每个标志,Omen 会报告:
- 标志键 - 代码中使用的标识符
- 提供商 - 使用的 SDK
- 引用位置 - 检查该标志的所有位置
- 陈旧情况 - 标志首次和最后一次修改的时间(基于 git 历史记录)
**自定义提供商:**对于内部功能标志系统,可在 omen.toml 中定义自定义 tree-sitter 查询:
[[feature_flags.custom_providers]]
name = "feature"
languages = ["ruby"]
query = '''
(call
receiver: (constant) @receiver
(#eq? @receiver "Feature")
method: (identifier) @method
(#match? @method "^(enabled\\?|get_feature_flag)$")
arguments: (argument_list
.
(simple_symbol) @flag_key))
'''
为何重要:Meinicke 等人(2020)研究了开源项目中的功能标志,发现标志所有权(引入标志的开发者也负责移除它)与更短的标志生命周期相关,有助于控制技术债务。Rahman 等人(2018)研究了 Google Chrome 的 12,000 多个功能开关,发现它们虽然能支持快速发布和灵活部署,但也会带来技术债务和额外的维护负担。定期审查标志可以避免代码库变成充斥着未使用开关的迷宫。
> [!TIP] > 每月审查一次功能标志。实验用标志超过 90 天、发布用标志超过 14 天后应予以移除。在 CI 流水线中跟踪标志的过期情况。Repository Score - 综合健康评分(0-100)
Omen 会计算综合仓库健康评分(0-100),结合多个分析维度。这能快速概览代码库质量,并在 CI/CD 中设置质量门禁。
评分组成:
| 组成项 | 权重 | 衡量内容 |
|---|---|---|
| 复杂度 | 25% | 超出复杂度阈值的函数占比 |
| 重复代码 | 20% | 代码克隆比例,并采用非线性惩罚曲线 |
| SATD | 10% | 按严重程度加权的 TODO/FIXME 密度,以及未解决的存根(todo!()、NotImplementedError 等) |
| TDG | 15% | 技术债务梯度综合评分 |
| 耦合 | 10% | 循环依赖、SDP 违规和不稳定性 |
| 代码异味 | 5% | 相对于代码库规模的架构异味 |
| 内聚性 | 15% | 面向对象代码库的类内聚性(LCOM) |
归一化理念:
每项指标都归一化到 0-100 的范围,分数越高始终越好。归一化函数遵循以下设计原则:
- 公平 - 严重程度相近的不同指标会得到相近的分数
- 经过校准 - 依据 SonarQube、CodeClimate 和 CISQ 的行业基准
- 非线性 - 对轻微问题轻度扣分,对严重问题大幅扣分
- 考虑严重程度 - 根据影响为事项加权,而不只是计数
例如,SATD(自认技术债务)采用按严重程度加权的评分:
- 严重(SECURITY、VULN):权重为 4 倍
- 高(FIXME、BUG):权重为 2 倍
- 中(HACK、REFACTOR):权重为 1 倍
- 低(TODO、NOTE):权重为 0.25 倍
这样可以避免低严重程度事项(如文档中的 TODO)不公平地拉低分数。
TDG(技术债务梯度)则通过分析每个文件的结构复杂度、语义复杂度、重复模式和耦合情况,提供互补视角。
用法:
# Compute repository score
omen score
# JSON output for CI integration
omen -f json score
调整阈值:
对于真实世界的代码库而言,达到 100 分几乎不可能。请根据代码库情况在 omen.toml 中设置切合实际的阈值:
[score]
fail_under = 70.0
运行 omen score 查看当前分数,然后将阈值设为略低于这些分数的值。随着时间推移逐步提高阈值。
使用 Lefthook 在提交时强制执行:
添加到 lefthook.yml:
pre-push:
commands:
omen-score:
run: omen score
这样可以防止推送不符合质量阈值的代码。
重要性: 单一健康评分可用于设置质量门禁、跟踪长期趋势,并快速评估代码库。加权综合评分能确保关键问题(缺陷、复杂度)比表面问题产生更大影响。
提示
从可实现的阈值开始,随着代码库质量的提升逐步提高阈值。在遗留代码中,重复代码往往是最难改善的指标。
语义搜索
通过自然语言发现代码
按含义而非仅按关键词搜索代码库。Omen 使用 TF-IDF 引擎,采用次线性 TF、平滑 IDF 和双词组分词,根据自然语言查询查找语义相似的代码。无需外部模型、API 密钥或 GPU。
# Build the search index
omen search index
# Discard cached entries and rebuild the complete index
omen search index --force
# Search for code
omen search query "database connection pooling"
omen search query "error handling middleware" --top-k 20
omen search query "validation" --min-score 0.5
omen search query "authentication" --files src/auth/,src/middleware/
# Cross-repo search
omen search query "retry logic" --include-project /path/to/other-repo
# Filter by complexity
# (via MCP: semantic_search with max_complexity parameter)
工作原理:
- 符号提取 - 使用 tree-sitter 从代码库中提取函数
- 感知 AST 的分块 - 在语句边界处分割长函数,使每个分块都聚焦且独立。保留父级类型上下文(类、结构体、impl)。
- TF-IDF 索引 - 构建稀疏向量索引,并使用 L2 归一化余弦相似度。典型代码库的索引耗时约为 1-2 秒。
- 增量更新 - 仅重新索引自上次运行以来发生变化的文件
- 去重 - 每个符号在结果中只出现一次(保留得分最高的分块)
查询默认最多返回 10 个结果,并丢弃相似度得分低于 0.3 的匹配项。索引存储在 .omen/search.db 中;后续索引运行只会更新内容哈希发生变化的文件。
功能:
- HyDE 搜索 - 将假设的代码片段作为查询,以获得更好的匹配结果(可通过 MCP
semantic_search_hyde工具使用) - 复杂度筛选 - 从结果中排除复杂度较高的函数(MCP 工具上的
max_complexity参数) - 多仓库搜索 - 使用统一的 IDF 评分查询多个项目索引(
--include-project) - 单函数指标 - 结果包含可用的圈复杂度和认知复杂度
性能:
| 指标 | 值 |
|---|---|
| 索引时间 | 约 1-2 秒(1,400 个符号) |
| 查询时间 | 约 250 毫秒 |
| 存储 | SQLite,位于 .omen/search.db |
| 依赖项 | 无外部依赖(纯 Rust TF-IDF) |
重要性: 传统的 grep/ripgrep 查找精确匹配。语义搜索则能找到即使命名不同、含义相同的代码。询问“我们如何验证用户输入”,即可找到名为 sanitize_params、check_request 或 validate_form 的函数。
提示
大规模重构后或开始使用新的代码库时,运行
omen search index。之后的运行会增量更新索引。
变异测试 - 通过代码变异测试套件的有效性
变异测试通过对代码进行小幅修改(变异),并检查测试是否失败,来衡量测试套件发现错误的能力。“被杀死”的变异体表示测试发现了错误;“存活”的变异体则表示错误可能漏过测试。
21 种变异算子:
| 类别 | 算子 | 变异内容 |
|---|---|---|
| 核心 | CRR、ROR、AOR、COR、UOR | 字面量、关系运算符、算术、条件、单目运算 |
| 高级 | SDL、RVR、BVO、BOR、ASR | 语句删除、返回值、边界、位运算、赋值 |
| Rust | BorrowOperator、OptionOperator、ResultOperator | 借用语义、Option/Result 处理 |
| Go | GoErrorOperator、GoNilOperator | 错误处理、nil 检查 |
| TypeScript | TSEqualityOperator、TSOptionalOperator | ===/==、可选链 |
| Python | PythonIdentityOperator、PythonComprehensionOperator | is/==、列表推导式 |
| Ruby | RubyNilOperator、RubySymbolOperator | nil 处理、符号/字符串转换 |
功能:
- 并行执行 - 使用工作窃取的异步工作池,高效执行变异测试
- 等价变异体检测 - 基于 ML 的评分,用于识别语义等价的变异
- 覆盖率集成 - 解析 LLVM-cov、Istanbul、coverage.py 和 Go 覆盖率数据,以跳过未经测试的代码
- 增量模式 - 仅测试变更文件中的变异
- CI/CD 集成 - 质量门禁和 GitHub 集成
用法:
# Generate mutants (dry run) with default operators (CRR, ROR, AOR)
omen mutation --dry-run
# Run mutation testing with all operators
omen mutation --mode thorough
# Fast mode (excludes operators that produce more equivalent mutants)
omen mutation --mode fast
# Run with coverage data to skip untested code
omen mutation --coverage coverage.json
# Incremental mode for CI - only test changed files
omen mutation --incremental
# Control parallelism
omen mutation --jobs 8
# Output surviving mutants for investigation
omen mutation --output-survivors survivors.json
# Filter to specific files
omen mutation --glob "src/analyzers/*.rs"
# Analyze a dirty working tree (review the changes first)
omen mutation --allow-dirty
默认情况下,如果存在未提交的更改,变异测试将拒绝运行,以免在应用变异体时覆盖现有工作。只有在检查并保护好当前更改后,才使用 --allow-dirty。
基于 ML 的预测:
Omen 包含一个 ML 模型,它会从你的变异测试历史中学习,预测哪些变异体会存活。这可以实现两项优化:
- 跳过显而易见的击杀项 - 不要浪费时间测试模型确信会被捕获的变异体
- 更好地检测等价变异体 - 学习代码库中哪些模式会产生等价变异体
# Record results to history file for later training
omen mutation --record
# Train the model from accumulated history
omen mutation train
# Use trained model to skip high-confidence kills (saves time)
omen mutation --skip-predicted 0.95
# Use a custom model path
omen mutation --model path/to/model.json
训练工作流:
-
收集数据:在代码库上运行
omen mutation --record。每个变异体的结果(击杀/存活)都会追加到.omen/mutation-history.jsonl,并包含:- 变异体详情(运算符、位置、原始代码/变异后代码)
- 源代码上下文(变异前后各 5 行)
- 执行时间
-
训练模型:运行
omen mutation train训练预测器。模型会学习:- 代码库中针对各运算符的击杀率
- 与存活相关的代码模式特征权重
-
使用预测:后续运行会自动加载
.omen/mutation-model.json。使用--skip-predicted 0.9跳过预测击杀概率 >90% 的变异体。
CI 工作流示例:
# Weekly: full run with recording
omen mutation --record --mode thorough
# After accumulating history: train model
omen mutation train
# Daily CI: fast run using predictions
omen mutation --incremental --skip-predicted 0.95
说明
默认情况下,
.omen/目录会被 gitignore。如果要在团队中共享训练好的模型,请从.gitignore中移除.omen/mutation-model.json。
变异分数:
变异分数用于衡量测试套件的有效性:
mutation_score = killed_mutants / (total_mutants - equivalent_mutants)
| 分数 | 质量 | 含义 |
|---|---|---|
| > 80% | 优秀 | 测试套件强大,能捕获大多数 Bug |
| 60-80% | 良好 | 覆盖率尚可,但仍有一些需要解决的缺口 |
| 40-60% | 中等 | 存在明显的测试缺口 |
| < 40% | 较差 | 测试遗漏了许多潜在 Bug |
**重要性:**代码覆盖率能告诉你测试期间运行了哪些代码,但不能说明测试是否真正验证了行为。如果缺少断言,一个函数即使达到 100% 覆盖率,变异分数也可能是 0%。Jia and Harman(2011) 表明,变异测试与故障检测高度相关。Papadakis et al.(2019) 证明,变异测试优于其他测试充分性标准。
提示
在 CI 中从
--mode fast开始,以便快速获得反馈;定期运行--mode thorough进行全面分析。使用--coverage,避免在未经测试的代码上浪费时间。
MCP 服务 - 通过 Model Context Protocol 集成大模型工具
Omen 内置了一个 Model Context Protocol(MCP)服务,可将所有分析器作为工具提供给 Claude 等大模型使用。这样,AI 助手便能通过标准化的工具调用直接分析代码库。
可用工具:
context- 面向 Agent 的仓库概览和导航提示outline- 低 Token 消耗的导入、类和函数概览complexity- 圈复杂度和认知复杂度satd- 自承认技术债检测stubs- 不完整/占位实现检测(只读;不会作为门禁)deadcode- 未使用的函数和变量churn- Git 文件变更频率clones- 代码克隆检测defect- 文件级缺陷概率(PMAT)changes- 提交级变更风险(JIT)diff- 分支差异风险分析tdg- 技术债梯度分数graph- 依赖关系图生成hotspot- 高变更频率与高复杂度文件temporal- 同时变更的文件ownership- 代码所有权和巴士因子cohesion- CK 面向对象指标repomap- 按 PageRank 排序的符号映射smells- 架构异味检测flags- 功能标志检测和过时状态检测score- 综合健康度评分(0-100)semantic_search- 自然语言代码搜索get_symbol- 获取一个符号的源代码、签名、位置、关系和复杂度impact- 传递调用方/被调用方影响范围分析semantic_search_hyde- HyDE 风格搜索(使用假设代码片段进行查询)
重要性: LLM 在使用结构化工具而非解析非结构化输出时效果最佳。MCP 为 Claude Desktop 和其他兼容的助手提供标准接口,而分页可确保大型结果不会超出上下文预算。
提示
将 omen 配置为 AI 助手中的 MCP 服务,即可使用自然语言查询,例如“查找最复杂的函数”或“显示技术债务热点”。
支持的语言
Go、Rust、Python、TypeScript、JavaScript、TSX/JSX、Java、C、C++、C#、Ruby、PHP、Bash(以及 tree-sitter 支持的其他语言)
安装
Homebrew(macOS/Linux)
brew install panbanda/brews/omen
Cargo 安装
cargo install omen-cli
Docker
# Pull the latest image
docker pull ghcr.io/panbanda/omen:latest
# Run analysis on current directory
docker run --rm -v "$(pwd):/repo" ghcr.io/panbanda/omen:latest -p /repo all
# Run specific analyzer
docker run --rm -v "$(pwd):/repo" ghcr.io/panbanda/omen:latest -p /repo complexity
# Get repository score
docker run --rm -v "$(pwd):/repo" ghcr.io/panbanda/omen:latest -p /repo score
提供适用于 linux/amd64 和 linux/arm64 的多架构镜像。
下载二进制文件
从发布页面下载预构建的二进制文件。
从源代码构建
git clone https://github.com/panbanda/omen.git
cd omen
cargo build --release
# Binary at target/release/omen
快速入门
# Run all analyzers
omen all
# Check out the analyzers
omen --help
# Minify JSON onto one line (the flag is global and may follow a subcommand)
omen -f json score --compact
# Include rustc dead-code diagnostics; cargo check executes build scripts,
# so use this only on trusted repositories
omen deadcode --cargo-check
顶级命令包括 complexity、satd、stubs、deadcode、churn、clones、defect、changes、diff、tdg、graph、hotspot、temporal、ownership、cohesion、repomap、smells、flags、score、mcp、all、context、report、search、mutation、outline、impact 和 symbol。运行 omen <command> --help 可查看该命令当前的选项。
远程仓库扫描
无需手动克隆,即可分析任何公开的 GitHub 仓库:
# GitHub shorthand
omen -p facebook/react complexity
omen -p kubernetes/kubernetes satd
# With specific ref (branch, tag, or commit SHA)
omen -p agentgateway/agentgateway --ref v0.1.0 all
omen -p owner/repo --ref feature-branch all
# Full URLs
omen -p github.com/golang/go all
omen -p https://github.com/vercel/next.js all
# Shallow clone for faster analysis (static analyzers only)
omen -p facebook/react --shallow all
Omen 会将仓库克隆到临时目录,运行分析,并自动清理。--shallow 标志使用 git clone --depth 1 加快克隆速度,但会禁用基于 git 历史记录的分析器(churn、ownership、hotspot、temporal coupling、changes)。
配置
自动配置发现会加载被分析仓库中的 omen.toml,然后加载 .omen/omen.toml 中的 TOML 配置。显式指定 --config <PATH> 时,可使用 TOML(无法识别扩展名时的默认格式)、YAML(.yaml 或 .yml)或 JSON(.json)。OMEN_ 环境变量会覆盖文件中的值;嵌套键使用双下划线,例如 OMEN_COMPLEXITY__CYCLOMATIC_ERROR=25。
接受的顶级键包括 exclude、exclude_built_assets、complexity、satd、churn、duplicates、hotspot、score、feature_flags、temporal 和 changes。将 omen.example.toml 复制为 omen.toml 或 .omen/omen.toml,然后按需自定义。配置结构体会拒绝未知键(包括未知的嵌套键),因此拼写错误会报错,而不会被静默忽略。
提示
使用 Claude Code?运行
setup-configskill,分析你的仓库并生成omen.toml,其中包含针对你的技术栈的智能默认配置,包括检测到的功能标志提供方和特定语言的排除模式。
GitHub Action
Omen 提供 GitHub Action,可自动分析 PR。它会在每个拉取请求上运行差异风险分析和健康度评分。
完整工作流
name: Omen Analysis
on:
pull_request:
permissions:
contents: read
pull-requests: write
issues: write
jobs:
omen:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: panbanda/[email protected]
id: omen
with:
version: latest
path: .
comment: true
label: true
label-template: 'risk: {{level}}'
label-color-low: '0e8a16'
label-color-medium: 'fbca04'
label-color-high: 'd93f0b'
check: true
check-threshold: high
summary: true
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Print results
run: |
echo "Risk: ${{ steps.omen.outputs.risk-level }} (${{ steps.omen.outputs.risk-score }})"
echo "Health: ${{ steps.omen.outputs.health-grade }} (${{ steps.omen.outputs.health-score }})"
重要
fetch-depth: 0是必需的,因为基于历史记录的分析需要完整的 git 历史记录。启用comment时需要pull-requests: write,启用label时需要issues: write。禁用这些功能的工作流可以省略相应的写入权限。
说明
请将此 action 固定到某个发布标签(最新版本为
omen-v4.26.1),并在有新版本发布时更新。上方的version: latest输入用于控制 action 下载的 omen 二进制文件版本,与 action 标签无关。
输入
| 输入 | 默认值 | 描述 |
|---|---|---|
version | latest | 要安装的 Omen 版本 |
path | . | 要分析的仓库路径 |
comment | false | 发布或更新固定的 PR 评论 |
label | false | 添加风险级别标签 |
label-template | risk: {{level}} | 标签名称模板({{level}} 会替换为 low、medium 或 high) |
label-color-low | 0e8a16 | 低风险标签的十六进制颜色(不含 #) |
label-color-medium | fbca04 | 中风险标签的十六进制颜色(不含 #) |
label-color-high | d93f0b | 高风险标签的十六进制颜色(不含 #) |
check | false | 风险达到阈值时失败 |
check-threshold | high | 触发失败的风险级别(low、medium、high) |
summary | true | 将风险、变更规模、健康状况和组件分析结果写入作业摘要 |
github-token | ${{ github.token }} | 用于获取发布信息并管理已启用的 PR 评论和标签的令牌 |
输出
所有输出都可用于传递给后续步骤:
| 输出 | 示例 | 描述 |
|---|---|---|
risk-score | 0.42 | 差异风险评分(0.0 - 1.0);在拉取请求之外为空 |
risk-level | medium | 风险级别(low、medium、high);在拉取请求之外为空 |
health-score | 76.9 | 健康评分(0 - 100) |
health-grade | C | 健康等级(A - F) |
diff-json | {...} | 完整的 omen diff JSON;在拉取请求之外为空 |
score-json | {...} | 完整的 omen score JSON |
如果 JSON 输出超过 GitHub Actions 的输出大小限制,action 会返回临时文件路径,而不是内联 JSON。使用者应接受这两种形式。
MCP 服务
Omen 内置了 Model Context Protocol(MCP)服务,可将所有分析器作为工具提供给 Claude 等大模型使用。这样,AI 助手就能直接分析代码库。
该服务仅使用 stdio 传输:omen mcp 和 omen mcp --transport stdio 等效。默认情况下,工具路径被限制在配置的仓库根目录内;对于确实需要更广泛文件系统访问权限的客户端,可使用 omen mcp --allow-external-paths 退出这一限制。MCP 服务没有主机或端口选项。
Claude Desktop
将以下内容添加到 Claude Desktop 配置文件中(macOS 上的路径为 ~/Library/Application Support/Claude/claude_desktop_config.json):
{
"mcpServers": {
"omen": {
"command": "omen",
"args": ["mcp"]
}
}
}
Claude Code
claude mcp add omen -- omen mcp
用法示例
配置完成后,你可以向 Claude 提问:
- “分析此代码库的复杂度”
- “查找 src 目录中的技术债”
- “哪些热点文件需要重构?”
- “显示此项目的人员流失风险”
- “查找应该移除的过期功能标志”
Claude Code 插件
Omen 可作为 Claude Code 插件使用,提供由分析结果驱动的技能,引导 Claude 执行代码分析工作流。
安装
/plugin install panbanda/omen
使用 /skills 验证安装,并查看可用的 Omen 技能。
前置条件
技能会直接调用 omen CLI,因此需要将 omen 二进制文件放在 PATH 中(请参阅安装)。安装插件时,每个插件都会通过 mcpServers 声明自动注册 Omen MCP 服务(command: omen、args: ["mcp"]),因此无需手动添加命令;如果愿意,也可以手动配置(请参阅上文的 MCP 服务部分)。
真实世界的代码仓库分析示例
examples/repos/ 目录包含热门开源项目的全面健康报告,展示了 Omen 在不同语言和项目类型中的能力。
已分析的代码仓库
| 代码仓库 | 语言 | 健康评分 | 关键洞察 |
|---|---|---|---|
| tiangolo/fastapi | Python | 91/100 (A) | 得分最高:内聚性、复杂度和耦合度均满分;重复代码来自不同版本文档中的示例 |
| gin-gonic/gin | Go | 87/100 (B) | 简洁、成熟的 Web 框架;耦合度和代码异味均满分,内聚性是主要提升空间 |
| discourse/discourse | Ruby | 84/100 (B) | 代码库最大(超过 14K 个文件),但技术债、复杂度和缺陷管理表现强劲;耦合度拖累了得分 |
| excalidraw/excalidraw | TypeScript | 77/100 (C) | 复杂度健康且重复代码较少;耦合度和架构异味拉低了得分 |
| BurntSushi/ripgrep | Rust | 74/100 (C) | 架构和复杂度表现出色;重复代码和 SATD 得分较低,反映出这是一个成熟且长期维护的代码库 |
报告展示的内容
1. 健康评分明细 每份报告都会展示如何根据各个组成部分(复杂度、重复代码、SATD、耦合度等)计算综合得分,并解释各项得分的原因。
2. 热点分析
报告会识别变更频繁且复杂度高的文件——这些文件是影响最大的重构目标。例如,gin 的 tree.go 因基数树路由的复杂性,热点得分为 0.54。
3. 技术债梯度(TDG) 根据累积的技术债,将文件评为 A-F。报告会解释低评级的成因,并确定清理工作的优先级。
4. PR 风险分析
每份报告都包含真实 PR 分析,展示 omen diff 的用法:
omen -f markdown diff --target main
gin-gonic/gin 的示例(#4420 - 添加转义路径选项):
Risk Score: 0.31 (MEDIUM)
Lines Added: 63
Lines Deleted: 2
Files Modified: 2
Risk Factors:
entropy: 0.084
lines_added: 0.118
num_files: 0.050
风险因素说明:
- 风险评分 - 低(< 0.2)、中(0.2-0.5)、高(> 0.5)
- 熵 - 变更的分散程度(0 = 集中,1 = 遍布各处)
- 新增/删除行数比例 - 净减少代码通常是个好迹象
- 修改文件数 - 文件越多,发生连锁问题的可能性越大
5. CI/CD 集成 报告包含用于质量门禁和 PR 风险评估的 GitHub Actions 工作流示例。
生成你自己的报告
对任意代码仓库运行全面分析:
# Local repository
omen score
omen hotspot
omen tdg
# Remote repository
omen -p facebook/react score
omen -p kubernetes/kubernetes hotspot
# PR risk before merging
omen diff --target main
# Track score trends over time
omen score trend --period monthly --since 6m
参与贡献
- Fork 此代码仓库
- 创建你的功能分支(
git checkout -b feature/amazing-feature) - 提交你的更改(
git commit -am 'Add amazing feature') - 将分支推送到远程(
git push origin feature/amazing-feature) - 创建 Pull Request
致谢
Omen 深受 paiml-mcp-agent-toolkit 启发——这是一个出色的 CLI,也是面向大模型工作流的一套全面代码分析工具。如果你正在进行严肃的 AI 辅助开发,值得一试。Omen 是面向希望使用精简分析器子集且不想引入额外依赖的团队所提供的轻量替代方案。如果你正在寻找以 Rust 为重点、可替代 Python 的 MCP/Agent 生成器,那也绝对值得一试。
许可证
Apache License 2.0 - 详情请参阅 LICENSE。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。