越界 / 不理想

只是打开看看的文件,也被它格式化了

Reformatted files it only opened to read

智能体对与任务无关的文件做了格式化、换行或风格调整,真正的改动淹没在空白噪音里。

适用于: Claude Code · Cursor · Codex · Copilot agent

症状

改了几百行,几乎没有一行是功能性的。任务根本不需要的文件里,引号风格、尾逗号或 import 顺序都变了。

为什么会这样

智能体会“顺手跑格式化或整理一下”(agent-review AR004)。claudecodeguides.com 称之为“格式化冲动”:Claude 只是打开文件看个 import,也会顺手清理。

示例

摘自 agent-review 分类(AR004)。

diff
- const x = 1; const y = 2; const z = 3
+ const x = 1
+ const y = 2
+ const z = 3

复制即用的修复

按你用的智能体挑选文件。规则片段用英文写,放进任何仓库都能用。

规则

Claude Code:加入 CLAUDE.md

CLAUDE.md
## No drive-by formatting
- Do not reformat, re-indent, reorder imports or change quote style in code you are not functionally changing.
- Run the formatter only on the lines or files you changed (for example `npx prettier --write <changed files>`),
  never on the whole repo, unless asked.
- If a file needs a format pass, propose it as a separate commit.
规则

Codex、Copilot 编码智能体等:加入 AGENTS.md

AGENTS.md
## No drive-by formatting
- Do not reformat, re-indent, reorder imports or change quote style in code you are not functionally changing.
- Run the formatter only on the lines or files you changed (for example `npx prettier --write <changed files>`),
  never on the whole repo, unless asked.
- If a file needs a format pass, propose it as a separate commit.
Cursor 规则

Cursor:项目规则

.cursor/rules/drive-by-reformat.mdc
---
description: "AgentFails: Reformatted files it only opened to read"
alwaysApply: true
---

## No drive-by formatting
- Do not reformat, re-indent, reorder imports or change quote style in code you are not functionally changing.
- Run the formatter only on the lines or files you changed (for example `npx prettier --write <changed files>`),
  never on the whole repo, unless asked.
- If a file needs a format pass, propose it as a separate commit.
钩子

只格式化改动过的文件(pre-commit)

lint-staged 只格式化暂存区的文件,提交钩子就不会把整个仓库重新格式化一遍。

package.json
{
  "lint-staged": {
    "*.{ts,tsx,js,jsx,css,md}": "prettier --write"
  }
}
CI / 脚本

Diff 预算检查(提交前或在 CI 中运行)

改编自 dev.to(yureki_lab)的 diff budget 文章。读取真实的 git diff,所以通过 shell 做的修改也逃不掉。用法:python3 scripts/diff_budget.py origin/main 6 150

scripts/diff_budget.py
#!/usr/bin/env python3
"""Fail if the diff vs BASE touches files outside .agent-scope or exceeds the budget."""
import fnmatch, os, subprocess, sys

base = sys.argv[1] if len(sys.argv) > 1 else "HEAD"
max_files = int(sys.argv[2]) if len(sys.argv) > 2 else 6
max_lines = int(sys.argv[3]) if len(sys.argv) > 3 else 150

allowed = []
if os.path.exists(".agent-scope"):
    allowed = [l.strip() for l in open(".agent-scope") if l.strip() and not l.startswith("#")]

out = subprocess.run(["git", "diff", "--numstat", base], capture_output=True, text=True, check=True).stdout
untracked = subprocess.run(["git", "ls-files", "--others", "--exclude-standard"],
                           capture_output=True, text=True, check=True).stdout.split()

files, lines, outside = 0, 0, []
rows = [r.split("\t") for r in out.strip().splitlines()] + [["0", "0", p] for p in untracked]
for added, deleted, path in rows:
    files += 1
    if added != "-":
        lines += int(added) + int(deleted)
    if allowed and not any(fnmatch.fnmatch(path, p) for p in allowed):
        outside.append(path)

problems = []
if outside:
    problems.append("out-of-scope files: " + ", ".join(outside))
if files > max_files:
    problems.append(f"{files} files changed (budget {max_files})")
if lines > max_lines:
    problems.append(f"{lines} lines changed (budget {max_lines})")

if problems:
    print("OVER BUDGET: " + "; ".join(problems))
    sys.exit(1)
print(f"within budget: {files} files, {lines} lines")

来源与致谢

相关条目

每周翻车

每周一个真实的智能体翻车案例和它的修复。免费、简短、不刷屏。