用 Claude Code 配置 Husky + lint-staged:提交前质量检查实战
用 Claude Code、Husky 和 lint-staged 在提交前自动运行 ESLint 与 Prettier,兼顾质量和速度。
Claude Code 很适合一次性修改多个文件:组件、测试、配置、文档可能在同一次会话里一起变化。问题是,变化越多,人工评审越容易被格式差异、未使用的 import、Markdown 空格这类细节分散注意力。真正需要人判断的是行为是否正确、接口是否清晰、产品意图是否被满足。
Husky + lint-staged 的价值就在这里。Git hook 是 Git 在提交、推送等动作前后执行的小脚本;Husky 负责把这些 hook 放进仓库,方便团队共享;lint-staged 只对已经 git add 的文件运行命令,避免每次提交都扫描整个项目。Claude Code hooks 是 Claude Code 生命周期里的自动化能力,和 Git hook 不是同一件事。它能提醒或限制 Claude Code 的行为,但不能保护普通终端、编辑器或其他工具产生的提交。
本文的目标不是把本地 pre-commit 变成完整 CI,而是把便宜、快速、确定的检查自动化,把昂贵的类型检查、测试和构建放到 pre-push 或 CI。
工作流全貌
我在实际项目里更推荐这种分层:pre-commit 做文件级检查,pre-push 做项目级检查,CI 做最终裁决。这样既能减少低级问题,又不会让开发者因为等待太久而习惯性使用 --no-verify。
flowchart LR
A["Claude Code 修改文件"] --> B["开发者选择并 git add"]
B --> C["Husky pre-commit"]
C --> D["lint-staged"]
D --> E["ESLint / Prettier"]
E --> F["commit"]
C --> G["类型检查、测试、构建放到 pre-push 或 CI"]
本文参考的官方资料包括 Husky Get started、lint-staged README、Git hooks 文档 和 Claude Code hooks reference。当前 Husky 推荐使用 npx husky init 初始化,lint-staged 则需要一个 pre-commit hook 和一份按文件 glob 映射命令的配置。
最小可运行配置
如果项目里的 ESLint 或 Prettier 还不稳定,先看 Claude Code ESLint 配置指南 和 Prettier 配置指南。Husky 只是入口,真正执行的是这些底层命令。
让 Claude Code 帮你改时,不要只说“加一个 pre-commit”。更好的提示是明确范围、速度和边界:
请给这个 Node.js/TypeScript 仓库加入 Husky 和 lint-staged。
pre-commit 只处理已暂存的 JS、TS、JSON、Markdown、CSS 文件。
在 pre-commit 里运行 ESLint 自动修复和 Prettier。
类型检查、测试、构建不要放进 pre-commit,请放到 pre-push 或 CI。
完成后列出我应该手动验证的命令。
手动安装也很直接:
npm install --save-dev husky lint-staged eslint prettier
npx husky init
npx husky init 会创建 .husky/pre-commit,并在 package.json 中加入或更新 prepare 脚本。把生成的 hook 内容改成下面这样:
#!/usr/bin/env sh
npx lint-staged
然后在 package.json 中加入 lint-staged 配置。如果已有 scripts,请合并键值,不要整段覆盖。
{
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint --fix .",
"format": "prettier --write .",
"format:check": "prettier --check .",
"prepare": "husky"
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix --max-warnings=0",
"prettier --write"
],
"*.{json,md,mdx,yml,yaml,css,scss}": [
"prettier --write"
]
}
}
验证时,故意制造一个格式不整齐的文件并暂存它:
git add src/example.ts
npx lint-staged --debug
git commit -m "chore: verify pre-commit checks"
--debug 会显示读取了哪份配置、哪些文件命中了 glob、实际执行了什么命令。排查失败时,把这段输出交给 Claude Code,比让它凭空猜测可靠得多。
更适合长期维护的配置
仓库变大后,我更倾向把配置移动到 lint-staged.config.mjs。这样 package.json 更干净,也可以写辅助函数。下面的例子会给文件名加引号,避免路径中有空格时命令失败。
// lint-staged.config.mjs
const shellQuote = (file) => `"${file.replaceAll('"', '\\"')}"`;
const joinFiles = (files) => files.map(shellQuote).join(" ");
export default {
"*.{js,jsx,ts,tsx}": (files) => [
`eslint --fix --max-warnings=0 ${joinFiles(files)}`,
`prettier --write ${joinFiles(files)}`,
],
"*.{json,md,mdx,yml,yaml,css,scss}": (files) =>
`prettier --write ${joinFiles(files)}`,
};
使用这个文件后,请删除 package.json 里的 lint-staged 字段。两份配置会制造维护成本,也会让 Claude Code 不确定应该更新哪一份。
三个真实使用场景
第一个场景是 TypeScript 产品应用。Claude Code 经常同时修改组件、测试和工具函数。pre-commit 只运行 ESLint 自动修复和 Prettier,就能去掉大量低价值 review 噪音。完整类型检查需要项目上下文,放到 pre-push 或 CI 更合适。
第二个场景是 Astro 或 Next.js 内容站。文章、MDX、JSON 元数据和 CSS 经常一起变。lint-staged 能让即将提交的文件保持统一格式,评审者可以专注看文章质量,而不是被空格和换行干扰。
第三个场景是 monorepo。不要在 pre-commit 里跑所有包的测试。更稳的做法是先对暂存文件做格式化和 lint,再让任务运行器或 CI 根据路径决定要跑哪些包的测试。给 Claude Code 的规则可以写成:pre-commit 要快,pre-push 做中等检查,CI 做完整检查。
commit-msg 与 pre-push 的分工
提交信息校验应该放在独立的 commit-msg hook。如果团队使用 Conventional Commits,可以用 commitlint。
npm install --save-dev @commitlint/cli @commitlint/config-conventional
// commitlint.config.mjs
export default {
extends: ["@commitlint/config-conventional"],
rules: {
"subject-max-length": [2, "always", 72],
},
};
#!/usr/bin/env sh
npx --no -- commitlint --edit "$1"
把最后一段放进 .husky/commit-msg。构建、测试和类型检查则放到 .husky/pre-push:
#!/usr/bin/env sh
npm run validate
对应的 package.json 可以这样组织:
{
"scripts": {
"typecheck": "tsc --noEmit",
"test:ci": "vitest run --coverage",
"build": "vite build",
"validate": "npm run typecheck && npm run lint && npm run format:check && npm run test:ci && npm run build"
}
}
这样 Claude Code 看到 pre-commit 失败时,会优先检查暂存文件;看到 validate 失败时,会知道这是项目级问题。
常见失败与坑
最大的坑是把 pre-commit 做得太重。每次提交等待两分钟,团队很快就会学会绕过它。一个几秒钟完成的 hook 才可能成为习惯。
第二个坑是部分暂存。lint-staged 面向暂存区,但同一个文件里可能还有未暂存修改。排查时同时看 git status --short、git diff --staged 和 npx lint-staged --debug。
跨 Windows、macOS、Linux 的团队还要注意换行。Husky hook 是 shell 脚本,建议用 LF 保存。
* text=auto eol=lf
*.cmd text eol=crlf
*.bat text eol=crlf
另一个不该接受的修复是 || true。它会把失败吞掉,让质量门禁看起来存在、实际上失效。更好的处理是缩短错误输出,把重任务移到 pre-push,或在 README 中写清楚修复命令。
实际验证结果
我用一个小型 TypeScript/Vite 项目测试了这套配置,故意加入格式问题、未使用 import 和 MDX 空格问题。pre-commit 只处理暂存文件,所以速度保持在几秒级;npx lint-staged --debug 能清楚显示命中的文件和命令。类型错误没有放进 pre-commit,而是在 npm run validate 的 pre-push 阶段拦截,这样提交节奏更自然。
总结
Husky + lint-staged 是 Claude Code 工作流里的实用安全网。让 pre-commit 保持轻量,把重检查交给 pre-push 和 CI,并把这个分工写进给 Claude Code 的提示词。这样 hook 不会成为麻烦的仪式,而会变成团队共享的质量底线。
下一步可以继续完善 ESLint 配置,并用 Prettier 配置 统一格式策略。
免费 PDF: Claude Code 速查表
输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。
我们会妥善保护你的信息,不发送垃圾邮件。
把 Claude Code 变成真正能带来结果的工作流
先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。
关于作者
Masa
专注 Claude Code 实务流程、团队导入和内容转化的工程师。
相关文章
Claude Code权限安全阶梯:逐步放开访问而不失控
从只读到有限编辑、验证命令和部署检查的 Claude Code 权限升级流程。
Claude Code 小PR证据包:让小改动真正可审查
用差异、验证命令、公开URL、CTA路径和回滚说明,把Claude Code的小PR变得可审查。
Claude Code 提交前 Review Gate:同时检查差异、测试、公开 URL 和 CTA
提交前用 Claude Code 审查差异范围、build、公开 URL、Gumroad 链接、咨询 CTA、缺少测试和无关文件。