☰
Cursor、Codex、Copilot 全适配:impeccable 设计检测器 Hook 接入实战
2026/10/11 15:54:47 网站建设 项目流程

Cursor、Codex、Copilot 全适配:impeccable 设计检测器 Hook 接入实战

【免费下载链接】impeccableThe design language that makes your AI harness better at design.项目地址: https://gitcode.com/GitHub_Trending/im/impeccable

AI 编程助手正在以肉眼可见的速度提升前端产出效率,但伴随而来的是一个让团队又爱又恨的现象——"AI 网页味":千篇一律的紫蓝渐变、嵌套圆角、低对比文案。GitHub 上已经 70K+ Star 的 impeccable 项目给出的解法很直接:不是继续给模型"讲道理",而是把一个确定性的设计检测器以 Hook 的形式挂进 AI 编程工具的工作流,让每次编辑都经过机械化的设计质检。

本文基于 impeccable 仓库源码,拆解其 Hook 系统的四个核心设计:两级规则如何划分"即时拦截"与"深度审查"、五种主流 harness 的钩子事件差异、.impeccable/config.json的项目级配置,以及值/文件/规则三级豁免策略的落地细节。读完你会清楚:这套系统为什么能把"设计审查"变成像 lint 一样确定、可审计的工程行为。

两级规则:即时拦截与深度审查的区别

impeccable 的 Hook 并不对所有规则一视同仁。设计问题天然分两种:一种是机械、明确、值得当场打断编辑的硬伤;另一种是依赖上下文和审美判断的软问题,更适合在回合结束后统一复盘。仓库用一张白名单把规则切成两个层级。

立即层(immediate tier)维护在 crates/foundation/src/registry.rs 的IMMEDIATE_TIER_RULES常量中,源码注释写明了筛选标准:"broken output(输出被破坏)、objective legibility failures(客观可读性失败)、single-property mechanical slop(单属性机械冗余)、design-system drift(设计系统漂移)"——每一条都是机械、无歧义、在编辑现场就值得修正的问题:

  • 输出损坏类:broken-image、text-overflow、body-text-viewport-edge
  • 对比度与可读性:low-contrast、gray-on-color、tiny-text
  • 单属性机械冗余:gradient-text、dark-glow
  • 设计系统漂移:design-system-font、design-system-color、design-system-radius、design-system-font-size

编辑现场(per-edit pass)只暴露这一层。其余规则(文案节奏、调色板与排版的品味、布局韵律)被 crates/hook/src/hook_lib.rs 的split_findings_by_tier归入 deferred 队列,留到 Stop 深度审查(deep pass)阶段对会话中 touch 过的所有 UI 文件跑一遍完整规则集,并且通过dedupe_against_cache与 per-edit 阶段已报过的发现去重——同一个问题不会让 AI 被提醒两次。

有一个关键细节值得注意:per_edit_tiering_active函数里,cursor和github两个 harness 被强制返回false。注释解释了原因:这两家的 stop 事件要么不稳定、要么无法把上下文回传给模型,深度审查根本没接通,如果还做分层 defer,非立即层规则会直接静默丢失。换句话说,分层是"能接到 Stop 事件"的 harness 的优化,不是所有工具都能享受。如果你希望每次编辑就跑全套规则,把配置里的hook.perEditRules设为"all"即可,这也是官方文档明确给出的恢复手段。

各 Harness 的钩子事件差异与配置

五种 harness 的钩子能力差异,是这套系统最"工程"的部分。impeccable 没有强行抹平差异,而是为每个 harness 生成符合其原生契约的 manifest。manifest 的生成逻辑集中在 crates/hook/src/admin.rs 的HOOK_MANIFEST_TARGETS与各*_manifest()函数中:

Harness钩子事件安装位置行为差异
Claude CodePostToolUse(matcher:Edit\|Write)+Stop.claude/settings.local.json(gitignored)编辑后推送简短提醒,Stop 时做深度审查
CursorpreToolUse.cursor/hooks.json写前拦截:阻止坏写入落地
CodexPostToolUse(matcher:Edit\|Write\|apply_patch)+Stop.codex/hooks.json需要/hooks信任审批
GitHub CopilotpostToolUse(matcher:edit\|create\|apply_patch).github/hooks/impeccable.json团队共享、提交到默认分支
Grok BuildPostToolUse+ StopadditionalContext.grok/hooks/impeccable.json编辑时静默,Stop 时才可见

Cursor 是唯一做写前拦截的。它的事件是preToolUse,对应二进制入口impeccable hook-before-edit(crates/hook/src/before_edit.rs)。这个入口的核心逻辑是把"将要写入的内容"在落盘之前跑一遍检测:它会从事件的tool_input里解析出content、streamContent、text,甚至能从 shell 命令中正则还原出重定向、tee、PythonPath.write_text、heredoc 的目标文件与内容(shell_redirect_path、shell_python_write_destination等一整套解析函数),再对"投影后的完整文件内容"做检测。检测出问题的响应是输出{"permission":"deny","user_message":...}拒绝该次写入,让 agent 在坏代码落地前重新考虑。为了防死循环,同一文件同一 finding 签名被连续拒绝超过EDIT_COUNT_THRESHOLD(6 次)后会自动降级为 allow 并附警告。

Codex 与 Claude Code 是"编辑后提醒"型:它们不拦截,而是在PostToolUse后向上下文注入一条短提醒——有新发现就给出修正提示,有遗留问题就再次提醒,干净文件给一句简短确认。Claude Code 还独占一个能力:stop_baseline模块(crates/hook/src/stop_baseline.rs)会记录会话中首次 Edit/Write 的文件"前像",Stop 深度审查用它区分"本次会话新引入的问题"和"本就存在的存量问题",避免把历史债务算到新改动头上。

Grok Build 是最特殊的一个。它的 PostToolUse 扫描只是标记 touched 文件(因为它会丢弃该 stdout,见 crates/hook/src/hook.rs 中harness == "grok"的分支),真正用户可见的通道是 Stop 事件的additionalContext。同时 Grok 会发两次 Stop:end_turn(可注入的门)和 observe-only 的shutdown,代码里用reason字段精确过滤,只扫end_turn。

Gemini 是例外中的例外:它不装 per-edit 检测器,只在.gemini/settings.json里装BeforeTool(把会话 id 注入build-phase的 shell 调用)和AfterAgent(构建完成提醒)。

事件识别的兜底逻辑也值得一看。crates/hook/src/hook_lib.rs 的resolve_harness在环境变量IMPECCABLE_HOOK_HARNESS未设置时,通过事件载荷的指纹反推 harness:Grok 的 camelCasetoolName/toolInput信封、Copilot 的toolArgs、Cursor 的conversation_id、Codex 的turn_id、Claude 作为最终兜底。这份兼容性也让 docs/HARNESSES.md 中那句"Source of truth"名副其实——impeccable 把每个 harness 的 spec 差异都收敛到了同一个运行时入口。

.impeccable/config.json项目级配置

所有 Hook 行为都由项目根的.impeccable/config.json统一驱动(个人覆盖写 gitignored 的config.local.json)。读取与合并逻辑在 crates/hook/src/hook_lib.rs 的read_config,逐层合并两个文件后再叠加默认值。配置分为两个命名空间:

hook键管 Hook 运行行为:

{ "hook": { "enabled": true, "quiet": false, "perEditRules": "immediate", "auditLog": ".impeccable/hook-audit.ndjson", "limits": { "maxFindings": 5, "maxChars": 8000, "maxFileBytes": 131072 } } }

其中perEditRules决定每编辑是只跑立即层("immediate",默认)还是全量("all");limits.maxFindings/maxChars限制单次注入上下文的体积,避免 Hook 输出喧宾夺主;maxFileBytes(默认 128KB)则是保护性上限——超大的文件不值得在编辑现场逐字扫描。

detector键管检测器的过滤与扩展:

{ "detector": { "ignoreRules": [], "ignoreFiles": [], "ignoreValues": [], "designSystem": { "enabled": true }, "advisoryRules": "exclude", "extensions": [{ "ext": ".blade.php", "engine": "html" }] } }

advisoryRules控制 advisory 级规则(如em-dash-overuse)是否参与输出;designSystem.enabled控制 DESIGN.md 设计系统约束是否生效。extensions是给服务端模板预留的口子:Blade、Twig、ERB、Handlebars 不在内置扩展表里(内置表见 crates/hook/src/hook_lib.rs 的ALLOWED_EXTS),声明后即可让 Hook 用对应引擎扫描它们——"engine": "html"走静态 HTML 引擎,"text"走纯文本检测。

环境变量是配置之上的"一键开关"层:IMPECCABLE_HOOK_DISABLED(整体禁用)、IMPECCABLE_HOOK_QUIET(静默确认消息)、IMPECCABLE_HOOK_HARNESS(强制指定 harness)、IMPECCABLE_CACHE_ROOT(把hook.cache.json/hook.pending.json这类可变状态迁移到项目外)。配置合并优先级依次是:默认值 <config.json<config.local.json< 环境变量。

豁免策略:值/文件/规则三级怎么设

再好的检测器也会有误报和合理例外。impeccable 的豁免哲学是"能多窄就多窄",并提供三级粒度。所有豁免统一走impeccable hooks管理命令(crates/hook/src/admin.rs),Hook 本身绝不写豁免配置——保证所有例外都沉淀在一个可审查的地方。

值级(ignore-value)——最窄,默认首选。针对某条规则在某个具体值上的误报:

impeccable hooks ignore-value overused-font Inter --shared --reason "User confirmed Inter is intentional"

支持--shared(写共享config.json)/--local(写个人config.local.json),--reason强制要求给出证据。对overused-font、bounce-easing这类"值敏感"规则,官方文档明确要求用值级豁免而不是规则级。有两个硬校验:"*"通配值必须带--file作用域(否则拒绝,防止误伤全项目);无法被提取器产出的"空转"值会被直接拒绝(synthetic_ignore_value校验),避免写一条永远不会生效的豁免。

文件级(ignore-file)——整文件跳过。用于整个文件都脱离设计审查范围的情形:fixture、生成产物、刻意保留的反模式演示:

impeccable hooks ignore-file "src/legacy/Card.tsx"

它会压制该文件上的所有规则——包括未来新增的规则。正因为杀伤面大,它被定位为"最后手段",一条规则吵就只豁免那条规则对应的值。

规则级(ignore-rule)——全项目关停一条规则。只有用户明确要求时使用。值得注意的一个设计:overused-font默认拒绝规则级豁免,必须显式加--all-values,因为"某个字体被过度使用"几乎总是值级问题:

impeccable hooks ignore-rule overused-font --all-values --reason "User asked to ignore overused fonts generally"

内联标记是第四通道,供"文件要离开仓库"的场景(导出的独立 HTML、邮件附件等)使用:impeccable-disable <rule>(整文件)、impeccable-disable-line/impeccable-disable-next-line(单行),任何注释语法均可,冒号或--后可带理由。

最后是分级处置原则(Triage),它把"人的判断"嵌进了 Hook 工作流:真实设计问题 → 修复,绝不为了绕过拦截而豁免;有证据的误报或授权例外 → 持久化最窄豁免并在回复中披露证据;拿不准 → 留一条问题问用户,一次一问。从值级到规则级,豁免的沉默面积越来越大,需要的人为确认也越来越多——这个梯度本身就是对"AI 自主豁免"边界的设计。

结语

impeccable 的 Hook 系统真正值得借鉴的,不是某一家的钩子配置,而是它对"设计审查到底该以什么节奏发生"的回答:机械性问题在编辑现场即时拦截,品味问题在回合结束后一次性复盘;能拦截的工具用preToolUse把坏写入挡在门外,只能提醒的工具就用克制、去重、可归因的提醒;豁免永远走最窄粒度并留下证据。当这些约束被一个 Rust 二进制统一执行时,"AI 网页味"就不再是模型审美的玄学问题,而是一套可以在 CI 和每个开发者的编辑循环里稳定运行的质量门禁。

【免费下载链接】impeccableThe design language that makes your AI harness better at design.项目地址: https://gitcode.com/GitHub_Trending/im/impeccable

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询