跳到正文
码上手 coding.chengyu.dev

Hooks:编辑后自动格式化

用 PostToolUse hook 在 Claude 每次编辑或写入文件后自动运行 Prettier 或 ruff,保证格式稳定,附可处理空格路径与多语言的脚本版本。

Claude CodeHooksPostToolUse格式化Prettier 更新于 2026-10-25

适用场景

你在 CLAUDE.md 里写了「改完文件请运行格式化」,Claude 大部分时候照做,偶尔忘记,于是提交里混进了格式变动。这类「每次必须发生」的事不该靠提醒,而应该交给 hook。

hook 是 Claude Code 在生命周期的固定节点上运行的 shell 命令,不经过模型决策。把格式化挂在「文件编辑工具执行成功之后」这个节点,就能保证每次改动都被格式化。

模板

写入项目的 .claude/settings.json。这个版本直接用 jq 取出被编辑文件的路径,交给 Prettier:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write --ignore-unknown"
          }
        ]
      }
    ]
  }
}

怎么用 / 怎么改

  • 前提。 机器上要有 jq(macOS 用 brew install jq,Debian 与 Ubuntu 用 apt-get install jq),项目里要有 Prettier。
  • 工作原理。 事件触发时,Claude Code 把事件数据以 JSON 通过标准输入交给你的命令。PostToolUse 的 matcher 按工具名过滤,Edit|Write 表示只在编辑或写入文件后触发;tool_input.file_path 就是被改的文件。
  • 验证方法。 让 Claude 在某个 JS 文件里加一行用单引号的字符串,然后打开文件,看是否被改成了 Prettier 默认的双引号。hook 成功时界面上不会提示任何内容。
  • 文件已生效。 直接编辑 settings 文件,运行中的会话会自动重新加载;运行 /hooks 可以浏览已配置的所有 hook。

脚本版:处理空格路径与多语言

路径含空格时 xargs 会拆错,也无法按文件类型选择工具。改用脚本。保存为 .claude/hooks/format.sh,并执行 chmod +x .claude/hooks/format.sh:

#!/bin/bash
FILE=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0

case "$FILE" in
  *.ts|*.tsx|*.js|*.jsx|*.json|*.css|*.md|*.astro)
    npx prettier --write "$FILE" >/dev/null 2>&1 || true
    ;;
  *.py)
    ruff format "$FILE" >/dev/null 2>&1 || true
    ;;
esac
exit 0

然后把 settings 里的 command 换成脚本,还可以加超时和提示文字:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/format.sh",
            "timeout": 30,
            "statusMessage": "正在格式化…"
          }
        ]
      }
    ]
  }
}

常见问题或注意事项

  • 脚本必须可执行。 忘记 chmod +x 是最常见的原因。hook 没触发时,先用 claude --debug 或会话里的 /debug 查看日志。
  • Bash 命令改的文件不会触发。 如果 Claude 用 sed 或重定向改了文件,Edit|Write 的匹配不会命中。要覆盖这种情况,可以改用 FileChanged 事件监听特定文件,或者在 Stop 事件里每轮结束后统一格式化。
  • 退出码有含义。 退出 0 表示没有异议;退出 2 表示阻止,并把标准错误输出作为反馈交给 Claude。其他非零退出码在多数事件里只会让操作继续并给出一条 hook 错误提示。格式化 hook 不应该阻止任何事,所以示例脚本总是 exit 0。
  • 想拦截而不是整理。 例如禁止修改 .env,应该用 PreToolUse 事件,脚本检查 tool_input.file_path,命中就写标准错误并 exit 2。官方指南有完整示例。
  • 团队共享要保证工具存在。 把 jq、Prettier 的安装步骤写进 README 或 CLAUDE.md,否则同事的 hook 会静默失败。
  • worktree 中的路径。 脚本用 $CLAUDE_PROJECT_DIR 引用,它始终指向启动会话的项目根;而被编辑的文件路径来自输入 JSON,所以格式化的是正确的文件。

参考来源

Esc

输入关键词开始搜索,例如「CLAUDE.md」「MCP」「部署」