1. 项目概述这不是一个工具而是一套可落地的开源代码审查方法论“open-code-review”这个标题乍看像某个新开源项目的代号但结合当前全网热搜词——CLI、LLM、git、codex cli、trae cli、prompt injection attack、密钥泄露防护、embedding 区别、diff.mnemonicprefix 等高频关键词它实际指向一个正在快速成型的工程实践范式以开源精神重构代码审查流程用 CLI 工具链 LLM 智能体 Git 原生能力构建透明、可审计、可复现、零密钥暴露风险的自动化审查体系。它不是封装好的黑盒软件而是一组经过生产验证的组合策略Git 钩子触发、本地 CLI 执行、LLM 模型调用完全离线或可控代理、结构化输出存档、diff 精准锚定、审查结论与原始 commit 绑定。我从去年开始在三个中型团队落地这套方案从最初用 shell 脚本硬编排到如今稳定运行在 CI/CD 流水线和开发者本地 pre-commit 中核心目标始终没变让每次代码变更的审查过程本身成为可被任何人 clone、audit、fork、改进的“开源资产”。它解决的不是“要不要审代码”而是“怎么审才真正可信、可追溯、不引入新风险”。适合两类人一是技术负责人想摆脱对 SaaS 审查平台的依赖二是资深开发者厌倦了把 API Key 塞进各种 GUI 插件里更愿意用 git log --oneline 一眼看清谁在哪次提交里改了哪条规则。它不替代人工 review但把重复性判断、风格一致性检查、基础安全漏洞扫描、文档同步校验这些“确定性工作”彻底从会议和 Slack 讨论中剥离出来让人类 reviewer 只聚焦于架构权衡、业务逻辑漏洞、边界条件设计——这才是 code review 应该有的样子。2. 整体设计思路为什么必须放弃“LLM Web UI”老路2.1 根本矛盾LLM 能力与审查可信度的天然张力当前市面上绝大多数“AI code review”产品本质是把 LLM 当作一个增强版的语法检查器塞进 Web IDE 插件里。用户点开一个 PR后台悄悄把 diff 发给某个云上模型返回几条带 emoji 的建议然后就完了。这种模式有三个致命缺陷而 open-code-review 的设计正是为堵住这三处漏洞第一审查过程不可见。你永远不知道模型看到了什么上下文——是只看了改动行还是加载了整个文件抑或偷偷把相邻模块的代码也喂给了它更可怕的是某些 SDK 会默认开启“自动上下文扩展”你没写 prompt它自己去 fetch 了 remote repo 的 master 分支。这意味着你的私有代码可能在你不知情的情况下被上传到第三方服务。我们曾用 tcpdump 抓包证实过某知名插件在用户未勾选“启用智能上下文”时仍会静默请求 /api/v1/context?refmaster。第二结果不可复现。LLM 输出具有随机性temperature 0同一份 diff上午跑一次返回“建议拆分函数”下午跑一次说“当前结构合理”。CI 流水线里如果依赖这种输出做 gate checkbuild 会间歇性失败工程师只能加 sleep 或 retry最终演变成玄学调试。我们团队早期试过用固定 seed 强制 deterministic结果发现模型底层 tokenizer 对换行符、空格数量极其敏感哪怕 diff 输出里多一个 \r\nembedding 向量就全变了。第三责任无法归属。当 AI 建议导致线上故障责任在谁是写代码的人是 review 的人还是那个返回错误建议的模型法律和工程伦理上都无解。而 open-code-review 的核心哲学是所有审查动作必须发生在开发者本地或公司内网所有输入输出必须落盘为 Git object每一次审查结论必须能通过 git blame 追溯到具体 commit 和执行环境。换句话说审查不是“服务”而是“代码变更的一部分”。2.2 架构选型CLI 作为唯一入口Git 作为唯一存储后端基于上述原则整个系统放弃 Web Server、放弃数据库、放弃任何中心化调度器。全部能力收敛到一个 CLI 工具我们内部叫 ocr-cli它的输入只有三样东西git rev-parse HEAD获取当前 commit hashgit diff --no-prefix HEAD~1生成标准 unified diff.ocr/rules.yaml本地配置文件定义哪些规则启用、LLM 模型路径、prompt 模板输出也只有一样一个 JSONL 文件每行一个审查项路径固定为.ocr/review-commit-hash.jsonl然后自动执行git add .ocr/review-*.jsonl git commit -m review: auto-generated for $COMMIT。这个设计带来四个硬性保障零网络外泄整个流程不发任何 HTTP 请求。LLM 模型如 CodeLlama-7b-Instruct完全本地加载用 llama.cpp 或 Ollama 运行若需调用云模型则强制走公司内网代理且所有请求 URL、headers、body 必须经git diff预处理——例如自动移除含password、secret_key、token字样的行再用正则匹配替换掉https://.*?/api/为[REDACTED_API_ENDPOINT]。绝对可复现.ocr/rules.yaml和.ocr/review-*.jsonl都是文本文件随代码一起 commit。任何人 checkout 到任意 commit运行ocr-cli review --commit hash得到的结果必然一致。我们甚至写了校验脚本sha256sum .ocr/review-*.jsonl | grep -q $(git show hash:./.ocr/review-*.jsonl | sha256sum)确保审查记录未被篡改。Git 原生集成审查结果不是飘在 IDE 里的气泡提示而是真正的 Git object。git log --grepreview:能列出所有审查提交git show review-commit:.ocr/review-*.jsonl直接查看原始 JSONLgit difftool -y code-commit review-commit可对比代码变更与审查结论的时空关系。这使得 code review 从“过程”变成了“产物”和单元测试报告、coverage 数据一样是代码库不可分割的元数据。权限最小化CLI 不需要任何特殊权限。它不读取~/.gitconfig以外的任何用户配置不访问$HOME下的 dotfiles所有模型权重、prompt 模板、规则定义都限定在项目根目录下的.ocr/子目录。开发者可以放心地在共享机器上运行不必担心个人 token 泄露。2.3 为什么不用 Agent 框架LLM 就是工具不是同事最近热词里频繁出现 “agent vs LLM vs AI model” 的辨析很多人试图用 LangChain、LlamaIndex 构建“能自主思考的审查 agent”。我们实测过三轮结论很明确在 code review 场景下Agent 架构是过度设计且引入新的脆弱点。Agent 的核心价值在于多步骤决策闭环plan → act → observe → reflect但代码审查的本质是单次、确定性、强约束的 pattern matching。它不需要“规划下一步该查什么”因为 diff 的范围是固定的不需要“观察执行结果”因为 LLM 调用本身就是原子操作更不需要“反思失败原因”因为审查失败只有一种情况——模型崩溃此时直接 fallback 到静态规则引擎即可。我们曾用 LangChain 实现一个“自动补全缺失单元测试”的 agent它会先分析 diff再生成 test stub再调用 pytest 验证再根据失败信息重写。结果发现87% 的失败源于第一步——LLM 对 diff 的理解偏差。比如把if (user.role admin)误读成if (user.role admin)JS vs Java 混淆后续所有步骤都建立在错误前提上。而纯 CLI 方案直接规避了这个问题输入就是标准 diff 文本输出就是 JSONL中间没有“状态”没有“记忆”没有“推理链”自然就没有“幻觉传导”。更重要的是Agent 框架通常依赖大量外部工具调用shell command、http request、file I/O这极大增加了 prompt injection 攻击面。NDSS 2026 那篇关于 “prompt injection attack to tool selection in llm agents” 的论文其 PoC 正是利用 GitHub PR description 里的恶意 markdown诱导 agent 执行rm -rf /。而我们的 CLI 严格遵循 Unix philosophy每个命令只做一件事且输入输出皆为纯文本。ocr-cli review的输入是 diff 字符串输出是 JSONL 字符串中间不 spawn 任何子进程不读写任何非指定路径的文件——攻击面被压缩到极致。3. 核心细节解析CLI 如何精准驱动 LLM 完成审查任务3.1 Diff 预处理从 Git 输出到 LLM 友好上下文的转换艺术LLM 并不天生理解git diff的语义。原始 diff 输出包含大量元信息index、old mode、new mode、diff --git还有各种 Git 特有的标记 -12,5 15,7 。直接把这些喂给模型效果极差——模型会把-符号当成数学运算把当作段落分隔符甚至把文件路径/src/main/java/com/example/Service.java解析成 URL。我们必须做三层清洗第一层语义剥离用正则提取出纯粹的“变更内容”移除所有 Git 元数据。关键正则如下# 移除 header 行index、old mode、new mode sed /^index\|^diff --git\|^---\|^/d # 移除 hunk header ... sed /^/d # 移除空行保留代码逻辑所需的空行 sed /^$/d但这还不够。 System.out.println(hello);这样的行是 Git 标记不是代码语法。我们需要把它转换成 LLM 能理解的“添加”语义。第二层语义标注我们发明了一种轻量级标注协议用前缀代替符号ADDED:替换表示新增行REMOVED:替换-表示删除行CONTEXT:保留无符号行表示上下文代码FILE:插入文件路径如FILE: src/main/java/com/example/Service.java转换后一段 diff 变成FILE: src/main/java/com/example/Service.java CONTEXT: public class UserService { CONTEXT: public User getUserById(Long id) { ADDED: if (id null) { ADDED: throw new IllegalArgumentException(id cannot be null); ADDED: } CONTEXT: return userRepository.findById(id).orElse(null); CONTEXT: }这个格式被证明对 LLM 理解变更意图提升显著。我们在 CodeLlama-7b 上做 A/B 测试用原始 diff 提问“这段代码有什么问题”准确率 42%用标注后格式提问准确率升至 79%。原因很简单LLM 在预训练时见过海量ADDED:REMOVED:这类日志前缀但几乎没见过-在代码块中的孤立使用。第三层上下文裁剪与锚定LLM 输入长度有限CodeLlama-7b context window 仅 4096 tokens。一份大型 PR 的 diff 可能超万行。我们采用“滑动窗口 关键行锚定”策略关键行识别用 AST 解析器如 tree-sitter扫描 diff提取所有if、for、try、return、throw所在行号以及所有函数定义行public void methodName(。这些是逻辑密度最高的位置。窗口构建以每个关键行为中心向上取 3 行 context向下取 5 行 context合并去重后形成多个小片段。每个片段保证不超过 512 tokens。锚定注入在每个片段开头插入ANCHOR: [file:line]例如ANCHOR: UserService.java:42。这样模型输出的建议能精确回溯到源码位置。这个策略让我们能在 7B 模型上稳定处理 500 行的复杂变更而不会丢失关键上下文。实测表明相比简单截断前 N 行准确率提升 33%且 false positive误报下降 61%。3.2 Prompt 工程如何让 LLM 说出“人话”而非“AI话”很多团队卡在 prompt 设计上模型返回一堆华丽辞藻却不说清“哪一行有问题”“该怎么改”。根本原因是 prompt 没强制结构化输出。我们采用“三明治 prompt”结构底层约束层用 system prompt 锁死输出格式You are a senior Java developer reviewing code changes. Output ONLY valid JSONL (one JSON object per line). Each object MUST have: - file: string, exact file path from FILE: line - line: integer, exact line number from ANCHOR - severity: string, one of [critical, high, medium, low] - message: string, plain English explanation, NO markdown, NO code blocks - suggestion: string, concrete fix, as single-line code snippet if applicable NO OTHER FIELDS. NO EXPLANATION OUTSIDE JSONL.中层示例层提供 2 个高质量 few-shot 示例{file:UserService.java,line:42,severity:high,message:Null pointer risk: id parameter is used without null check before calling findById,suggestion:if (id null) { throw new IllegalArgumentException(\id cannot be null\); }} {file:UserService.java,line:45,severity:medium,message:Missing error handling: findById may return Optional.empty(), but no .orElseThrow() or .isPresent() check,suggestion:return userRepository.findById(id).orElseThrow(() - new EntityNotFoundException(\User not found\));}顶层任务层用 user prompt 注入当前 diff 片段Review this code change: FILE: UserService.java ANCHOR: UserService.java:42 CONTEXT: public class UserService { CONTEXT: public User getUserById(Long id) { ADDED: if (id null) { ADDED: throw new IllegalArgumentException(id cannot be null); ADDED: } CONTEXT: return userRepository.findById(id).orElse(null); CONTEXT: }这个 prompt 结构的关键在于system prompt 用“ONLY”“MUST”“NO OTHER”等绝对化措辞配合 JSONL 格式强制彻底关闭模型的自由发挥空间。我们测试过 GPT-4 和 CodeLlama发现只要 system prompt 足够强硬模型就会老老实实输出 JSONL哪怕它内心很想加一句“温馨提示建议补充单元测试”。few-shot 示例则教会模型什么是“高严重性”——不是语法错误而是运行时风险什么是“具体建议”——不是“请优化代码”而是给出可 copy-paste 的单行代码。3.3 密钥泄露防护LLM 调用时的“数据净化流水线”这是 open-code-review 最受关注的安全环节。热搜词里反复出现“使用 LLM 时如何防止密钥等鉴权信息泄露”绝非空穴来风。我们设计了四道防线防线一diff 静态扫描在 CLI 加载 diff 后、送入 LLM 前先用正则扫描所有行SECRETS_PATTERNS [ r(?i)(password|pwd|secret|token|api[_-]?key|access[_-]?key|auth[_-]?token)\s*[:]\s*[\]([^\])[\], r(?i)https?://[^/]:[^][^/], r(?i)BEGIN\sRSA\sPRIVATE\sKEY|BEGIN\sOPENSSH\sPRIVATE\sKEY ]一旦匹配立即终止本次审查并输出警告ERROR: Potential secret detected in diff at UserService.java:123 Line: private static final String API_KEY sk-abc123...; Action: Please remove secrets from code and use environment variables instead.注意这里不尝试“脱敏”如把sk-abc123替换成sk-***因为部分模型会把***当作通配符继续推理。最安全的做法是直接阻断。防线二LLM 输入过滤即使 diff 里没明文密钥也可能存在间接泄露。例如// UserService.java String url https://api.example.com/v1/users/ userId; HttpURLConnection conn (HttpURLConnection) new URL(url).openConnection(); conn.setRequestProperty(Authorization, Bearer getToken());这里的getToken()方法可能从环境变量读取但模型若看到Authorization和Bearer可能推测出认证方式。因此我们对所有含敏感词的行做模糊替换Authorization→Auth_HeaderBearer→Token_Schemehttps://api.→https://[REDACTED_API]/env.get(DB_URL)→env.get([REDACTED_ENV_VAR])防线三模型层沙箱本地运行的 LLM如 llama.cpp启用--no-mmap和--no-hugetlb参数强制内存锁定防止 swap 泄露Ollama 运行时加--gpu-layers 0确保不调用 GPU避免显存残留。最关键的是所有模型权重文件.gguf设置chmod 600仅 owner 可读且 CLI 启动时校验文件 SHA256防止权重被篡改注入后门。防线四输出后处理LLM 返回的 JSONL 可能包含意外泄露。例如模型在suggestion字段里写// Use System.getenv(API_KEY) instead。我们对每个suggestion字段再做一次正则扫描匹配到敏感词则替换为[REDACTED]并记录告警日志。这步看似多余实则是最后一道保险——毕竟我们无法 100% 信任任何 LLM 的输出。4. 实操过程从零搭建一个可运行的 open-code-review 环境4.1 环境准备三分钟完成 CLI 安装与基础配置整个流程不依赖 Python、Node.js 等通用运行时只依赖 Git 和一个预编译二进制。我们提供 macOS/Linux/Windows 三平台 release下载即用# macOS curl -L https://github.com/your-org/ocr-cli/releases/download/v1.2.0/ocr-cli-darwin-arm64 -o /usr/local/bin/ocr-cli chmod x /usr/local/bin/ocr-cli # Linux x86_64 wget https://github.com/your-org/ocr-cli/releases/download/v1.2.0/ocr-cli-linux-x86_64 -O /usr/local/bin/ocr-cli chmod x /usr/local/bin/ocr-cli # Windows (PowerShell) Invoke-WebRequest -Uri https://github.com/your-org/ocr-cli/releases/download/v1.2.0/ocr-cli-windows-amd64.exe -OutFile $env:ProgramFiles\ocr-cli.exe # Add to PATH via System Properties安装后验证ocr-cli --version # 输出 v1.2.0 ocr-cli --help # 显示完整命令列表接下来初始化项目# 进入你的 Git 仓库 cd /path/to/your/project # 创建 .ocr 目录自动创建 ocr-cli init # 查看生成的默认配置 cat .ocr/rules.yaml默认rules.yaml内容精简到 12 行只启用最基础的检查model: codellama-7b-instruct.Q4_K_M.gguf # 模型文件名需手动下载 prompt_template: java-review-v1.jinja # prompt 模板内置 3 种语言 rules: - id: null-check enabled: true severity: high - id: empty-catch enabled: true severity: medium - id: hardcoded-url enabled: true severity: low提示模型文件需单独下载。我们提供预量化 GGUF 文件Q4_K_M 精度约 3.8GB适配 16GB RAM 笔记本。下载地址在 GitHub release assets 里命名规范为codellama-7b-instruct.Q4_K_M.gguf。把它放到.ocr/models/目录下即可CLI 会自动识别。4.2 模型接入本地运行 CodeLlama 的实操细节与性能调优CodeLlama-7b-Instruct 是目前开源领域最适合 code review 的模型但直接运行常遇到 OOM 或速度慢问题。我们总结出一套“笔记本友好”配置内存优化默认 llama.cpp 会加载整个模型到 RAM。对于 7B 模型Q4_K_M 量化后需约 5.2GB RAM但实际运行时因 KV cache 占用16GB 笔记本仍可能 swap。解决方案是启用--mlock参数Linux/macOS或--use-mmapWindows让 OS 管理内存映射# 在 .ocr/config.yaml 中设置 llama: args: [--mlock, --threads, 4, --ctx-size, 2048]--threads 4限制 CPU 核心数避免拖慢其他任务--ctx-size 2048缩小 context window减少 KV cache 占用实测对 review 任务影响极小diff 通常 1000 tokens。GPU 加速可选如果你有 NVIDIA GPU用 CUDA 版本提速 3-5 倍# 下载 cuda-enabled llama.cpp binary curl -L https://github.com/ggerganov/llama.cpp/releases/download/master/llama-server-cuda-12.2-linux-x86_64 -o /usr/local/bin/llama-server chmod x /usr/local/bin/llama-server # 在 .ocr/config.yaml 中启用 llama: backend: cuda args: [--n-gpu-layers, 35, --threads, 2]--n-gpu-layers 35表示把前 35 层 offload 到 GPU7B 模型共 32 层留几层 CPU 处理更稳。注意CUDA 版本必须匹配驱动我们测试过 12.2 适配 RTX 3090/4090。响应速度实测在 M2 MacBook Pro16GB RAM上纯 CPU 模式处理一个 50 行 diff平均耗时 8.2 秒启用--mlock后降至 6.7 秒CUDA 加速后为 1.9 秒。这个速度已足够嵌入 pre-commit hook——我们设置pre-commit超时为 15 秒确保不影响开发者正常提交节奏。4.3 Git 集成pre-commit hook 与 CI 流水线的无缝对接open-code-review 的威力在于与 Git 深度绑定。我们提供两种集成方式方式一pre-commit hook推荐给开发者本地在项目根目录创建.pre-commit-config.yamlrepos: - repo: local hooks: - id: open-code-review name: Open Code Review entry: ocr-cli review --auto-commit language: system types: [python, java, javascript, typescript] pass_filenames: false然后安装 hookpip install pre-commit pre-commit install每次git commit时hook 会自动获取本次 commit 的 diff运行ocr-cli review将生成的.ocr/review-*.jsonl加入暂存区自动 amend 到当前 commitgit commit --amend --no-edit注意--auto-commit参数是关键。它让 CLI 在审查完成后自动执行git add .ocr/review-*.jsonl git commit --amend确保审查记录和代码变更在同一个 commit object 里。这样git log --oneline会显示类似a1b2c3d review: auto-generated for a1b2c3d清晰表明审查与代码的共生关系。方式二CI 流水线推荐给团队统一管控在 GitHub Actions 或 GitLab CI 中添加 jobreview-code: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 with: fetch-depth: 2 # 必须获取 parent commit 以生成 diff - name: Install ocr-cli run: | curl -L https://github.com/your-org/ocr-cli/releases/download/v1.2.0/ocr-cli-linux-x86_64 -o ocr-cli chmod x ocr-cli sudo mv ocr-cli /usr/local/bin/ - name: Run open code review run: | # 下载模型从私有 bucket wget https://your-bucket/codellama-7b-instruct.Q4_K_M.gguf -P .ocr/models/ ocr-cli review --commit ${{ github.event.pull_request.head.sha }} - name: Upload review report uses: actions/upload-artifactv3 with: name: code-review-report path: .ocr/review-*.jsonl这个 job 的输出 artifact 可被后续 job 读取用于生成 PR comment 或 blocking gate。我们用一个简单的 Python 脚本解析 JSONL统计severity: critical的数量0 则exit 1让 CI 失败。4.4 规则定制如何编写自己的审查规则与 prompt 模板.ocr/rules.yaml是 open-code-review 的“大脑”。它支持两种扩展方式自定义规则rule每个 rule 是一个独立的 YAML block定义触发条件和严重等级。例如添加一条检查“日志中是否包含 PII 信息”- id: pii-in-log enabled: true severity: critical patterns: - logger.info.*\\b(\\d{3}-\\d{2}-\\d{4}|\\d{11}|[A-Za-z0-9._%-][A-Za-z0-9.-]\\.[A-Z|a-z]{2,}\\b) message: PII data (SSN, phone, email) found in log statement suggestion: Use logger.debug() for debugging, or mask PII before loggingpatterns是正则数组CLI 在送入 LLM 前会先做一轮静态扫描。命中则直接生成 review item不调用 LLM速度极快。自定义 prompt 模板jinja2.ocr/prompts/目录下放.jinja文件。例如python-review-v1.jinja{% for chunk in diff_chunks %} FILE: {{ chunk.file }} ANCHOR: {{ chunk.file }}:{{ chunk.line }} {% for line in chunk.context %}CONTEXT: {{ line }}{% endfor %} {% for line in chunk.added %}ADDED: {{ line }}{% endfor %} {% for line in chunk.removed %}REMOVED: {{ line }}{% endfor %} {% endfor %} You are a senior Python developer reviewing code changes. Output ONLY valid JSONL...Jinja2 模板让 diff 结构化渲染更灵活。我们内置了 Java/Python/JS 三种模板差异主要在CONTEXT行的缩进处理和注释风格识别上。实操心得不要试图用一个 prompt 覆盖所有语言。Java 的public static void main(String[] args)和 Python 的def main():语法差异巨大LLM 很难泛化。分语言定制 prompt准确率提升 40% 以上。我们团队的做法是每个新语言加入先写 10 个典型 diff case手工写出期望的 JSONL 输出再反向推导 prompt最后用这 10 个 case 做回归测试。5. 常见问题与排查技巧实录那些踩过的坑和独门解法5.1 模型输出格式错乱JSONL 解析失败的 5 种原因与修复ocr-cli review报错Failed to parse JSONL output: Expecting property name enclosed in double quotes是新手最常遇到的问题。我们整理了真实场景中的 5 类原因及对应解法问题现象根本原因修复方案验证命令输出首行是{file:...但第二行是{file:...无换行LLM 生成了连续 JSON 对象未用\n分隔在 system prompt 中强制要求Output ONLY valid JSONL (one JSON object per line)并添加NO OTHER FIELDS. NO EXPLANATION OUTSIDE JSONL.echo {file:a}{file:b} | python -m json.tool会报错输出包含中文引号“file”模型在中文环境下输出全角标点在 CLI 中添加 post-processsed s/“//g; s/”//g; s/‘//g; s/’//gecho {file:a} | sed s/“//g | python -m json.tool应成功输出末尾有多余空格或换行模型在 JSONL 末尾加了\n\nCLI 解析时用strip()去除首尾空白再按\nsplitpython -c import sys; print([x.strip() for x in sys.stdin.read().split(\\n) if x.strip()])输出包含注释// This is a comment模型在 JSONL 里加了 JS 风格注释在 system prompt 中明确禁止NO COMMENTS. NO EXPLANATION OUTSIDE JSONL.echo {file:a} // comment | python -m json.tool会报错输出字段名大小写错误{File:a}模型未严格遵守file字段名在 CLI 解析层做字段 normalizeobj.get(file) or obj.get(File) or obj.get(FILE)python -c import json; djson.loads({\File\:\a\}); print(d.get(file) or d.get(File))最有效的预防措施是每次更新 prompt必须用ocr-cli review --dry-run测试 10 个不同 diff case人工检查输出是否 100% JSONL 合法。我们有个内部 checklist其中一条是“打开 VS Code用 JSON Language Mode 打开输出文件确认无红色波浪线”。5.2 Git diff 生成异常git diff --no-prefix HEAD~1返回空的 7 种场景ocr-cli review依赖准确的 diff 输入。当它返回空结果常见原因如下当前分支无 parent commit首次 commit 或 orphan branch。解法ocr-cli review --commit hash指定具体 commit而非依赖HEAD~1。Git 配置禁用了默认 diffgit config --global diff.mnemonicprefix false会导致git diff输出格式变化。解法CLI 内部强制加--no-prefix和--U0无 context 行不依赖 mnemonicprefix。工作区有未提交更改git diff HEAD~1会包含未暂存的修改。解法CLI 默认只处理git diff --cached HEAD~1即 staged changes避免污染。文件编码问题UTF-16 文件被 Git 识别为 binarydiff 为空。解法CLI 检测到Binary files提示时自动跳过该文件并记录 warning。Git LFS 跟踪的大文件LFS 文件 diff 显示为version https://git-lfs.github.com/spec/v1。解法CLI 识别 LFS marker跳过审查避免 LLM 处理无意义文本。submodule 变更git diff对 submodule 显示Subproject commit xxx。解法CLI 过滤掉含Subproject commit的行专注代码文件。Windows CRLF 换行符干扰git config --global core.autocrlf true导致 diff 出现^M。解法CLI 在预处理时统一转为 LFsed s/\r$//。独家技巧我们开发了一个ocr-cli debug-diff命令它会依次执行git diff --no-prefix HEAD~1、git diff --cached --no-prefix HEAD~1、git show HEAD~1:./ --name-only并对比输出自动诊断问题根源。运行一次比查文档快 10 分钟。5.3 性能瓶颈排查从 30 秒到 3 秒的 4 个关键优化点当ocr-cli review耗时超过 10 秒按以下顺序排查**第一优先