
为 OmO comment-checker 钩子扩展 exclude_patterns让 AI 注释质量检查告别误报【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent本篇文章围绕 oh-my-openagentOmO中 comment-checker 钩子的一个实战增强展开通过新增exclude_patterns配置项让注释检查器在保留检测 AI 注入的废话注释能力的同时放过Note:、TODO:这类合法技术注释从而消除高频误报。读完本文你将掌握该钩子的完整调用链配置 schema → hook 接线 → CLI runner → 核心二进制并能够复现文档中的全部代码变更与测试用例。背景comment-checker 钩子解决的问题在 OmO 的 OpenCode 插件packages/omo-opencode中AI Agent 执行write、edit、multiedit、apply_patch等文件写入工具时往往会顺手往代码里塞入大量AI 味注释——例如// Note: This was added to handle the edge case这类没有任何信息量的备忘录式注释业内俗称 AI slop。这些注释污染代码库是 PR review 时最令人头疼的问题之一。comment-checker 钩子正是为此而生它在工具执行前tool.execute.before登记待检查的文件与内容在工具执行后tool.execute.after调用独立的 comment-checker 二进制把本次写入的代码片段交给它做注释质量检查。若检测到疑似 AI 注入的注释就把警告消息追加到工具输出中让 Agent 立即看到并自我修正。核心接线逻辑位于 hook.ts。痛点Note:、TODO:等合法注释被误报问题在于注释检测器基于模式匹配// Note: Thread-safe by design、# Note: See RFC 7231这类完全合理、专业开发者也会写的技术注释很容易被误判为 AI 废话注释而触发警告。对于真实工程场景这会产生大量误报让开发者要么无视警告要么被迫关掉整个钩子反而放走了真正的 AI slop。本文所分析的代码变更文档位于.agents/skills/work-with-pr-workspace/iteration-1/eval-5/with_skill/outputs/code-changes.md给出的解决方案非常直接为 comment-checker 增加一个exclude_patterns正则数组配置命中排除模式大小写不敏感的注释不再参与检测。下面我们逐文件拆解这次改动。第一步扩展配置 Schema新增exclude_patternscomment-checker 的配置类型定义在 config/schema/comment-checker.ts当前仓库中的现状是仅支持custom_promptimport { z } from zod export const CommentCheckerConfigSchema z.object({ /** Custom prompt to replace the default warning message. Use {{comments}} placeholder for detected comments XML. */ custom_prompt: z.string().optional(), }) export type CommentCheckerConfig z.infertypeof CommentCheckerConfigSchema变更文档给出的目标状态是在该对象上新增一个可选数组字段export const CommentCheckerConfigSchema z.object({ /** Custom prompt to replace the default warning message. Use {{comments}} placeholder for detected comments XML. */ custom_prompt: z.string().optional(), /** Regex patterns to exclude from comment detection (e.g. [^Note:, ^TODO:]). Case-insensitive. */ exclude_patterns: z.array(z.string()).optional(), }) export type CommentCheckerConfig z.infertypeof CommentCheckerConfigSchema要点说明exclude_patterns是一个字符串数组每一项都是一个正则表达式直接以字符串形式传入 CLI 二进制大小写不敏感匹配文档注释明确标注 Case-insensitive典型用法是[^Note:, ^TODO:]即排除以Note:或TODO:开头的注释行两个字段均为optional()因此该配置对既有用户完全向后兼容——不配置任何排除模式时行为与现在一致。从实现结构看该 schema 在packages/omo-opencode/src/config/schema/下被统一导出最终注入 configuration.md 所记载的comment_checker配置段例如{ comment_checker: { custom_prompt: Your message. Use {{comments}} placeholder. } }新增字段后用户即可在同一配置段中同时设置custom_prompt与exclude_patterns。第二步改造runCommentChecker透传--exclude-pattern参数钩子侧真正发起 CLI 调用的函数是 cli.ts 中的runCommentChecker。当前仓库签名如下变更前状态export async function runCommentChecker(input: HookInput, cliPath?: string, customPrompt?: string): PromiseCheckResult { const binaryPath cliPath ?? resolvedCliPath ?? getCommentCheckerPathSync() // ... try { const args [binaryPath, check] if (customPrompt) { args.push(--prompt, customPrompt) }变更后文档给出的目标实现新增第四个参数excludePatterns?: string[]并在组装参数时循环追加--exclude-pattern标志export async function runCommentChecker( input: HookInput, cliPath?: string, customPrompt?: string, excludePatterns?: string[], ): PromiseCheckResult { const binaryPath cliPath ?? resolvedCliPath ?? getCommentCheckerPathSync() // ... try { const args [binaryPath, check] if (customPrompt) { args.push(--prompt, customPrompt) } if (excludePatterns) { for (const pattern of excludePatterns) { args.push(--exclude-pattern, pattern) } }理解这段改动的关键在于args数组的最终去向。runCommentChecker最终调用的是packages/comment-checker-core中的核心 runner见 runner.ts后者负责真正 spawn 二进制子进程const args [input.binaryPath, check] if (input.customPrompt ! undefined) { args.push(--prompt, input.customPrompt) } // ... const process options.spawn(args) process.stdin.write(JSON.stringify(input.hookInput)) process.stdin.end()即检查对象通过 stdin 以 JSON 形式传入--prompt/--exclude-pattern等选项通过命令行参数传入。同时该 runner 还负责了进程超时与安全清理默认timeoutMs为 30 秒超时先发SIGTERM再经killGraceMs默认 1 秒后升级为SIGKILL退出码0表示未检测到注释退出码2表示检测到注释此时 stderr 内容即警告消息会被规范化后追加到工具输出。因此--exclude-pattern标志会被原样传递到真实二进制。至于二进制内部如何使用该标志例如对每个被检测注释做正则匹配、命中则跳过属于独立 CLI 的职责钩子侧只负责配置 → 参数的正确映射。另外值得注意cli.ts 中二进制解析遵循「缓存路径 → 核心resolveCommentCheckerBinarynpm 包内 bin→ PATH 查找 → 惰性下载」的优先级且COMMENT_CHECKER_DEBUG1环境变量可开启调试日志写入系统临时目录comment-checker-debug.log。这些机制保证了即使二进制缺失钩子也会安全降级返回{ hasComments: false, message: }不会中断 Agent 的工具执行。第三步cli-runner.ts参数穿透参数需要一路从 hook 穿透到runCommentChecker中间层是 cli-runner.ts 中的两个函数processWithCli处理write/edit/multiedit和processApplyPatchEditsWithCli处理apply_patch。processWithCli的变更前签名export async function processWithCli( input: { tool: string; sessionID: string; callID: string }, pendingCall: PendingCall, output: { output: string }, cliPath: string, customPrompt: string | undefined, debugLog: (...args: unknown[]) void, ): Promisevoid { await withCommentCheckerLock(async () { // ... const result await runCommentChecker(hookInput, cliPath, customPrompt)变更后追加末尾可选参数excludePatterns?: string[]并继续透传export async function processWithCli( input: { tool: string; sessionID: string; callID: string }, pendingCall: PendingCall, output: { output: string }, cliPath: string, customPrompt: string | undefined, debugLog: (...args: unknown[]) void, excludePatterns?: string[], ): Promisevoid { await withCommentCheckerLock(async () { // ... const result await runCommentChecker(hookInput, cliPath, customPrompt, excludePatterns)processApplyPatchEditsWithCli采用完全相同的模式export async function processApplyPatchEditsWithCli( sessionID: string, edits: ApplyPatchEdit[], output: { output: string }, cliPath: string, customPrompt: string | undefined, debugLog: (...args: unknown[]) void, excludePatterns?: string[], ): Promisevoid { // ... const result await runCommentChecker(hookInput, cliPath, customPrompt, excludePatterns)值得注意的是cli-runner.ts 在调用真实二进制之前还有两道内置防线它们与exclude_patterns是互补关系值得理解净新增注释过滤hasNewCommentsOnly(oldText, newText)会先做行级对比只对新加入的注释行触发检查——如果注释本来就存在于旧代码中只是被整体重写不会误报会话级去重sessionLastWarning以 30 秒DEDUP_WINDOW_MS为窗口同一 session 最多每轮响应触发一次警告避免死循环式反复告警。此外所有对二进制的调用都被withCommentCheckerLock包裹保证同一时刻只有一个检查子进程在运行isRunning为 true 时后续调用直接跳过。第四步hook.ts 接线把配置落到调用链最终把用户配置接进来的位置是 hook.ts 中的createCommentCheckerHooks(config, cliRunner)。该函数返回tool.execute.before、tool.execute.after两个钩子config参数即来自上文 schema 校验后的配置对象。apply_patch分支的变更前await processApplyPatchEditsWithCli( input.sessionID, edits, output, cliPath, config?.custom_prompt, debugLog, )变更后在调用尾部追加config?.exclude_patternsawait processApplyPatchEditsWithCli( input.sessionID, edits, output, cliPath, config?.custom_prompt, debugLog, config?.exclude_patterns, )普通写文件分支tool.execute.after中处理 pendingCall 的部分同理// Before await processWithCli(input, pendingCall, output, cliPath, config?.custom_prompt, debugLog) // After await processWithCli(input, pendingCall, output, cliPath, config?.custom_prompt, debugLog, config?.exclude_patterns)到这里完整的调用链就打通了用户配置 { comment_checker: { exclude_patterns: [^Note:, ^TODO:] } } → zod schema 校验config/schema/comment-checker.ts → createCommentCheckerHooks(config)hook.ts → processWithCli / processApplyPatchEditsWithClicli-runner.ts → runCommentChecker(input, cliPath, customPrompt, excludePatterns)cli.ts → runCommentCheckerCore 组装 argscomment-checker-core/src/runner.ts → spawn(comment-checker check --exclude-pattern ^Note: ...) stdin 传入 hook input第五步测试用例用模拟二进制验证行为改动文档同时给出了完整的测试策略集中在 cli.test.ts新增用例追加在describe(runCommentChecker, ...)内和hook.apply-patch.test.ts中。测试的最大亮点是createScriptBinary辅助函数——它动态生成一个可执行的 shell 脚本Windows 下为.cmd来扮演真实二进制从而在不依赖真实 comment-checker 下载的情况下端到端验证参数传递与退出码语义。用例一配置排除后合法的Note:不再触发警告test(does not flag legitimate Note: comments when excluded, async () { // given const { runCommentChecker } await import(./cli) const binaryPath createScriptBinary(#!/bin/sh if [ $1 ! check ]; then exit 1 fi # Check if --exclude-pattern is passed for arg in $; do if [ $arg --exclude-pattern ]; then cat /dev/null exit 0 fi done cat /dev/null echo Detected agent memo comments 12 exit 2 ) // when const result await runCommentChecker( createMockInput(), binaryPath, undefined, [^Note:], ) // then expect(result.hasComments).toBe(false) })该测试脚本模拟了真实二进制的核心语义收到--exclude-pattern参数即返回退出码0无注释否则打印警告到 stderr 并返回退出码2有注释。据此断言hasComments false验证排除模式生效。用例二多个排除模式全部透传test(passes multiple exclude patterns to binary, async () { // given const { runCommentChecker } await import(./cli) const capturedArgs: string[] [] const binaryPath createScriptBinary(#!/bin/sh echo $ /tmp/comment-checker-test-args.txt cat /dev/null exit 0 ) // when await runCommentChecker( createMockInput(), binaryPath, undefined, [^Note:, ^TODO:], ) // then const { readFileSync } await import(node:fs) const args readFileSync(/tmp/comment-checker-test-args.txt, utf-8).trim() expect(args).toContain(--exclude-pattern) expect(args).toContain(^Note:) expect(args).toContain(^TODO:) })这个用例直接捕获子进程收到的全部 argv断言--exclude-pattern、^Note:、^TODO:都确实被传给了二进制——防止参数在透传链中被丢弃。用例三未配置排除模式时AI slop 依然被检测test(still detects AI slop when no exclude patterns configured, async () { // given const { runCommentChecker } await import(./cli) const binaryPath createScriptBinary(#!/bin/sh if [ $1 ! check ]; then exit 1 fi cat /dev/null echo Detected: // Note: This was added to handle... 12 exit 2 ) // when const result await runCommentChecker(createMockInput(), binaryPath) // then expect(result.hasComments).toBe(true) expect(result.message).toContain(Detected) })这是关键回归保障exclude_patterns只是白名单豁免默认行为检测并告警 AI 注释绝不能被削弱。假阳性场景专项测试变更文档还新增了一个独立的describe(false positive scenarios, ...)测试块覆盖三类典型场景合法技术注释// Note: Thread-safe by design在配置[^Note:]后hasComments falseRFC 引用注释# Note: See RFC 7231在配置排除后同样不再告警AI 备忘录注释// Note: This was added to handle the edge case在未配置排除时仍返回hasComments true。三者合在一起精确刻画了本次改动的边界排除的是注释模式而不是注释内容语义开发者可以按团队规范自定义豁免列表。apply_patch 集成测试最后hook.apply-patch.test.ts中新增的用例验证配置从 hook 一路穿透到 CLIit(passes exclude_patterns from config to CLI, async () { // given const hooks createCommentCheckerHooks({ exclude_patterns: [^Note:, ^TODO:] }) const input { tool: apply_patch, sessionID: ses_test, callID: call_test } const output { title: ok, output: Success. Updated the following files:\nM src/a.ts, metadata: { files: [ { filePath: /repo/src/a.ts, before: const a 1\n, after: // Note: Thread-safe\nconst a 1\n, type: update, }, ], }, } // when await hookstool.execute.after // then expect(processApplyPatchEditsWithCli).toHaveBeenCalledWith( ses_test, [{ filePath: /repo/src/a.ts, before: const a 1\n, after: // Note: Thread-safe\nconst a 1\n }], expect.any(Object), /tmp/fake-comment-checker, undefined, expect.any(Function), [^Note:, ^TODO:], ) })该用例通过 mockprocessApplyPatchEditsWithCli并断言其最后一个实参为[^Note:, ^TODO:]直接证明了config.exclude_patterns已被正确注入调用链末尾。配置使用示例结合 OmO 的配置体系详见 configuration.md 中 Comment Checker 一节升级后的完整配置形如{ comment_checker: { custom_prompt: Detected AI-injected comments:\n{{comments}}\nPlease remove them or replace with meaningful technical notes., exclude_patterns: [^Note:, ^TODO:, ^FIXME:, ^HACK:] } }参数语义总结配置项类型说明示例custom_promptstring可选替换默认警告文案{{comments}}占位符会被替换为检测到的注释 XMLUse {{comments}} placeholder.exclude_patternsstring[]可选命中即豁免检测的正则数组大小写不敏感[^Note:, ^TODO:]小结这次改动的工程价值从文档描述的整组变更5 个文件的修改可以看到一个清晰的设计取向向后兼容exclude_patterns是可选参数从 schema 到 hook 全程以追加尾部参数的方式透传不配置时行为零变化职责清晰钩子侧只做配置 → CLI 参数的映射正则匹配语义交由独立二进制实现测试用模拟脚本解耦依赖回归可控新增测试覆盖排除生效、多模式透传、默认行为不回归、假阳性专项、apply_patch 集成五个维度把误报修复建立在可验证的测试之上可维护的调用链config schema → hook.ts → cli-runner.ts → cli.ts → comment-checker-core/runner.ts每一层职责单一后续若再新增配置项例如排除特定文件路径可以完全复用这套透传模式。对日常使用者而言结论很简单在comment_checker配置中按团队注释规范声明exclude_patterns即可在保留 AI slop 检测的同时让Note:、TODO:这类专业注释安静通过 PR 检查。相关源码与文档路径索引配置 schemaconfig/schema/comment-checker.tsCLI 调用入口cli.ts参数穿透层cli-runner.ts钩子接线hook.ts核心 runner 与退出码语义comment-checker-core/src/runner.ts单元测试cli.test.ts、hook.apply-patch.test.ts配置文档docs/reference/configuration.md【免费下载链接】oh-my-openagentOmO: Just type mass ulw keyword with your prompt. Now you are the master of graph engineering.项目地址: https://gitcode.com/gh_mirrors/oh/oh-my-openagent创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考