impeccable 设计检测 Hook 完全指南:为 Claude Code、Codex、Cursor、Grok 与 GitHub Copilot 配置逐次编辑的 UI 质量守门员
impeccable 设计检测 Hook 完全指南为 Claude Code、Codex、Cursor、Grok 与 GitHub Copilot 配置逐次编辑的 UI 质量守门员【免费下载链接】impeccableThe design language that makes your AI harness better at design.项目地址: https://gitcode.com/GitHub_Trending/im/impeccableImpeccable 的设计检测 Hookdesign detector hook是随 Impeccable 技能分发的自动化设计审查管道每当 Agent 直接编辑.tsx、.jsx、.html、.vue、.svelte、.astro、.css等设计相关文件时它都会在后台运行设计检测器把机械性、无歧义的设计问题破图、溢出裁剪、对比度不足、渐变文字、发光阴影、设计系统漂移以系统提示的形式推回 Agent 上下文。本文基于 .opencode/skills/impeccable/reference/hooks.md 展开结合仓库中的 Rust 实现crates/hook、crates/foundation系统讲解 Hook 的双层规则机制、五类 AI 编码助手的接入差异、.impeccable/config.json完整配置、impeccable hooks管理命令、以及 findings 的三分法分诊流程——读完即可在自己的项目里启用、调优并正确消化检测结果。Hook 是什么一次编辑后的机械设计检查Impeccable 的 Hook 是一个纯机械的自动化检查器Every hook is a mechanical pass。它不评判品味、不评估排版节奏只拦截那些客观、无歧义、值得打断一次编辑的问题。它关注的不是设计好不好看而是这次改动是否产生了可证伪的设计缺陷。从实现看Hook 逻辑由三个独立入口组成Rust 原生实现对应 JS 时代的hook.mjs/hook-lib.mjs/hook-admin.mjscrates/hook/src/hook.rsrun_hookPostToolUse 逐次编辑通道与run_stop_hookStop 深度通道总是以退出码 0 结束stdout 要么是单个 JSON 文档要么为空crates/hook/src/before_edit.rshook-before-edit即 Cursor 的preToolUse写入闸门同样总是退出 0输出恰好一个 JSON 文档{permission:allow}或 deny 载荷crates/hook/src/admin.rsimpeccable hooks管理命令status/on/off/ignore-rule/ignore-file/ignore-value/reset。扫描的目标扩展名由常量ALLOWED_EXTS定义见 crates/hook/src/hook_lib.rspub const ALLOWED_EXTS: [str] [ .tsx, .jsx, .html, .htm, .vue, .svelte, .astro, .css, .scss, .sass, .less, .ts, .js, ];各助手的接入形态差异同一个检测器在不同 AI 编码助手里以不同形态工作这是阅读本文最容易混淆、也最需要先建立认知的部分助手接入事件行为Claude CodePostToolUse编辑后向上下文推入一段简短系统提醒findings 给修正提示待处理问题给再提示干净 UI 文件给简短确认除非hook.quietCodexPostToolUse同上首次需通过/hooks批准GitHub CopilotpostToolUse同上因 Copilot 的 stop 类事件无法把上下文反馈给模型保持每次编辑都跑完整检测器CursorpreToolUse在写入落地前检查拟写内容只有真实检测到问题时才拒绝deny放行干净写入时保持沉默Grok BuildPostToolUseStopPostToolUse 只标记被触碰的文件Grok 会丢弃该 stdout不要指望逐次编辑提醒findings 在 Stop 事件的additionalContext上浮出值得注意的两个助手特例Cursor的拒绝消息对 Agent 可见为工具错误The denial message is visible to the agent as the tool errorAgent 可以在坏写入落地前重新考虑。Grok在end_turn之后还会触发一次只读的 Stopreason: shutdown必须跳过它、只扫描end_turn——这正是 crates/hook/src/hook.rs 中run_stop_hook对reason ! end_turn直接skipped: stop-reason返回的原因。双层规则机制逐次编辑层与 Stop 深度层Hook 的规则分为两层two tiers逐次编辑层immediate tier只在编辑时上浮立即可修的问题——机械、无歧义、值得为它中断一次编辑的缺陷。Stop 深度层deep pass其余一切文案节奏、调色板与字体品味、布局节奏全部推迟到会话结束的Stop事件在那里对本次会话触碰过的每个UI 文件跑完整规则集并与逐次编辑层已报告的 findings 去重后一次性上浮。本次会话没剩下任何可报告内容时Stop 深度通道静默结束——对应 crates/hook/src/hook.rs 中fresh_groups.is_empty()时的skipped: stop-clean。immediate tier 具体包含哪些规则immediate tier 的规则清单被抽到crates/foundation/src/registry.rs的IMMEDIATE_TIER_RULES常量之所以放在 foundation 而不是 hook crate是因为 hook crate 是 native-only而 wasm 消费者需要同一份列表见 crates/foundation/src/registry.rspub const IMMEDIATE_TIER_RULES: [str] [ // Broken output. broken-image, text-overflow, clipped-overflow-container, body-text-viewport-edge, // Objective contrast / legibility failures. low-contrast, gray-on-color, tiny-text, // Single-property mechanical slop, trivial to fix at the edit site. gradient-text, dark-glow, // Design-system drift compounds if not corrected at edit time. design-system-font, design-system-color, design-system-radius, design-system-font-size, ];分类逻辑一目了然破图/溢出/裁剪/视口越界这类坏输出、对比度/灰度字/过小字号这类客观可读性失败、渐变文字/深色发光这类单属性机械瑕疵、以及设计系统字体/颜色/圆角/字号漂移这类越拖越难修的系统偏差。规则分组与设计意图完全对应源码注释Every one of them is mechanical, unambiguous, and cheap to correct at the edit site.恢复每次编辑跑全部规则如果希望每次编辑都跑完整规则集放弃双层分层在.impeccable/config.json中设置{ hook: { perEditRules: all } }perEditRules接受all或immediate默认值解析逻辑见 crates/hook/src/hook_lib.rs。run_hook中per_edit_tiering_active(config, harness)决定是否把 findings 切成(immediate, deferred)两堆deferred 部分仅touch_file记入缓存留给 Stop 通道见 crates/hook/src/hook.rs。另外哪些助手会收到 Stop 深度通道Claude Code、Codex、Grok Build 会分发原生Stop事件所以有深度通道Cursor 的 stop hook 不会被稳定分发由 pre-write 闸门覆盖GitHub Copilot 的 stop 类事件无法把上下文反馈给模型所以这两家保持逐次编辑跑完整检测器。配置中心.impeccable/config.json与config.local.jsonHook 是按项目per project开关的。所有运行时配置都落在统一的 Impeccable 配置里.impeccable/config.json项目共享Hook 运行时设置放在hook键下共享的检测器忽略项放在detector键下.impeccable/config.local.json开发者私有gitignored开发者级覆盖包括 CLI 记录的安装同意决策hook.consent。从源码看read_config依次读取config.json再读config.local.json后者覆盖前者见 crates/hook/src/hook_lib.rsHookConfig::default()给出了完整的默认值见 crates/hook/src/hook_lib.rs。hook键的完整参数键类型默认值作用enabledbooltrue设为false关闭 Hookquietboolfalse设为true静默干净/待处理确认clean/pending acksauditLogstring无指向一个 NDJSON 日志文件路径perEditRulesstringimmediateall或immediate控制逐次编辑层规则集advisoryRulesstringexcludeinclude或exclude是否纳入 advisory 类规则limits.maxFindingsnumber5单次输出的最大 findings 数limits.maxCharsnumber8000单次输出的最大字符数UTF-16limits.maxFileBytesnumber131072超过该字节数的文件跳过扫描一个最小但完整的启用配置示例{ hook: { enabled: true, quiet: false, perEditRules: immediate, limits: { maxFindings: 5, maxChars: 8000, maxFileBytes: 131072 } }, detector: { designSystem: { enabled: true }, ignoreRules: [], ignoreFiles: [], ignoreValues: [] } }注意limits.maxFileBytes的上限检查在 crates/hook/src/hook.rs超过时记录skipped: too-large并附带bytes审计字段。默认 128 KB 意味着超大文件不会被逐次扫描。环境变量覆盖legacy以下 legacy 环境变量仍然有效设置时优先于配置值IMPECCABLE_HOOK_DISABLED1一次性关闭跟随 shell 作用域IMPECCABLE_HOOK_QUIET1等价于hook.quiet: trueIMPECCABLE_HOOK_LOGpath等价于hook.auditLog。run_hook中环境变量检查先于配置读取IMPECCABLE_HOOK_DISABLED为 truthy 时直接返回skipped: env-disabled见 crates/hook/src/hook.rs。truthy 判定接受1|true|yes|on大小写不敏感见 crates/hook/src/hook_lib.rs。为 Blade / Twig / ERB / Handlebars 声明模板扩展名内置扩展名列表之外的服务端模板栈Blade、Twig、ERB、Handlebars默认会被跳过。声明方式是在detector.extensions下逐个添加{ detector: { extensions: [ { ext: .blade.php, engine: html }, { ext: .html.erb, engine: html } ] } }每个扩展一条记录engine选择分析器html用于标记模板text用于 JS/TS/CSS 类文件默认html匹配规则是按文件名结尾匹配match against the end of the filename所以.blade.php、.html.erb这类双扩展名可用配置只做追加内置列表始终生效。实现细节在 crates/hook/src/hook_lib.rsnormalize_extension_entries会为缺ext的裸字符串自动补点前缀match_configured_extension对每个 entry 做name.ends_with(ext)并选择最长匹配。detector.extensions没有对应的 admin 动作属于唯一允许直接手改的字段见下文约束。手动 CLI 扫描与 Hook 的关系手动npx impeccable detect扫描默认使用同一套项目过滤配置detector.ignoreRules、detector.ignoreFiles、detector.ignoreValues、detector.designSystem.enabled。但hook.enabled只控制自动 Hook 执行不影响手动 CLI 扫描。npx impeccable detect --no-config ...忽略项目配置/上下文跑一次裸检测npx impeccable ignores ...在 CLI 上直接对同样的 detector ignores 做增删改查CRUD。各助手 Hook 清单的安装位置支持的五类助手Hook manifest 都是项目本地文件通过impeccable hooks on安装/修复助手Manifest 路径备注Claude Code.claude/settings.local.jsongitignored机器本地移入共享的settings.json也会被就地识别Codex.codex/hooks.json首次需通过/hooks批准Cursor.cursor/hooks.json需在 Settings - Hooks 确认已启用Grok Build.grok/hooks/impeccable.json需要/hooks-trust或--trustGitHub Copilot.github/hooks/impeccable.json团队共享的已提交文件Copilot CLI 与云 Agent 都读它CLI 在文件提交到仓库默认分支后生效这些 manifest 的内容由 crates/hook/src/admin.rs 的HOOK_MANIFEST_TARGETS定义例如 Claude Code 的 manifest 同时注册PostToolUsematcherEdit|Write5 秒超时和Stop30 秒超时状态消息 Design deep passCursor 的 manifest 只注册preToolUse调用impeccable hook-before-edit。impeccable hooks管理命令动作路由表第一条参数是动作缺省为status。完整动作表动作作用status打印当前状态、共享/本地配置路径、被忽略的规则/文件/值、环境变量覆盖on在.impeccable/config.json写enabled: true在本地配置记录 hook consent 为 accepted并在技能已安装时安装/修复各助手的 manifestoff在.impeccable/config.json写enabled: falseignore-rule id向detector.ignoreRules追加id对overused-font必须加--all-values在整个项目范围内抑制该规则ignore-file glob向detector.ignoreFiles追加glob对匹配文件抑制每一条规则ignore-value id value [--shared] [--reason ...]向共享.impeccable/config.json追加规则/值抑制ignore-value id value --local [--reason ...]向.impeccable/config.local.json追加私有规则/值抑制ignore-value id * --file glob [--file glob...]只在匹配文件中关闭某一条规则其他地方保持激活--file可重复也支持--fileglob/--filesglob裸*不带--file会被拒绝reset删除项目配置、去重缓存与 Cursor 待处理队列并从on安装过的每个 provider manifest 中移除 Hook 条目已提交的 Copilot 文件也在内on从不写共享settings.json所以它不会被碰调用形式脚本位于 .opencode/skills/impeccable/scripts/impeccable.impeccable/skills/impeccable/scripts/impeccable hooks action [args...]从实现看动作分派在 crates/hook/src/admin.rsrun解析首参缺省status未知动作打印合法动作列表并返回 1on内部调用set_enabled(rt, cwd, true)——写共享配置、写consent: accepted到本地配置、再repair_hook_manifests对已存在的 skill 目录逐个合并/修复/备份 manifestreset则删除配置、缓存、pending 队列并对每个 manifest 做prune_impeccable_hook_from_manifest清理。几个值得注意的参数行为ignore-rule overused-font不带--all-values会直接报错提示改用ignore-value overused-font font或补--all-values见 crates/hook/src/admin.rsignore-value的裸*不带--file会被拒绝并提示改用ignore-rule见 crates/hook/src/admin.rsignore-file不接受--reason因为detector.ignoreFiles只存 globignore-value会拒绝惰性精确条目——若该规则根本无法产出该值则报错提示改用文件级抑制见 crates/hook/src/admin.rs。status输出格式由 crates/hook/src/admin.rs 生成显示 state、shared/local 文件路径含(malformed; ignored)标注、ignoreRules/ignoreFiles/ignoreValues、maxFindings/maxChars、IMPECCABLE_HOOK_DISABLED覆盖状态与缓存文件路径。命令流Flow技能如何转发给 admin 脚本从用户参数解析动作未给动作时默认status调用 admin 脚本并把用户输出原样透传.opencode/skills/impeccable/scripts/impeccable hooks action [args...]若动作是off追加一行提示Done. New edits will not trigger the design hook in this project until you run/impeccable hooks on.若动作是on追加Done. The design hook will fire after the next Edit/Write on a UI file.若动作是ignore-value/ignore-file/ignore-rule只打印脚本输出默认作用域是共享的.impeccable/config.json只有用户明确要求私有例外时才加--local。若动作是status只打印脚本输出用户没追问就不要加注释。Findings 三分法分诊TriageHook 本身从不直接写忽略配置——所有例外都必须经由impeccable hooks。每个 finding 都归入三种结局之一真实设计问题Real design problem修掉它。永远不要为了跳过修复或强行放行被拦截的写入而添加 ignore。有把握的误报或合规例外Confident false positive or sanctioned exception自行持久化最窄的 ignore并在回复中披露。判据是你能说出名字的证据有意的 demo/fixture、坏设计的文档化、字面意义或领域适配的动效如弹跳的球、或用户已确认的选择。把证据写进--reason格式为who decided: evidence只有用户确实确认过才写 user confirmed。不确定Unsure保留 finding用一行问题问用户。只问一次——一行问题的成本远低于 Hook 在每次后续编辑时重新触发。自助self-serve止步于ignore-valueignore-file与ignore-rule抑制面太大不能凭自己的判断添加必须先问用户。如何选择最窄的例外若 finding 行给出了ignore-value rule value组合就把它原样传给impeccable hooks ignore-value并带上--reason。默认写入共享.impeccable/config.json对值特定的 finding如overused-font、bounce-easing用ignore-value抑制具体值不要用ignore-rule overused-font去抑制一个具体字体若 finding 没有值特定命令如side-tab把该规则限定到文件ignore-value id * --file path。先跑npx impeccable detect path看实际触发什么只有当整个文件都不在设计审查范围内fixture、生成产物、故意的 slop demo时才用ignore-file path。它会永久静默该文件的每一条规则——包括还没写出来的规则。一个只有一条吵闹规则的真实 UI 表面应该用上面的文件级值抑制只有用户要求在整个项目抑制整条规则时才用ignore-rule id。对广泛的字体抑制仅在用户要求一般性忽略 overused 字体时使用ignore-rule overused-font --all-values默认优先使用配置式 ignore上述命令它们把抑制项集中在一个可审查的位置。只有当豁免必须跟随单个文件离开仓库生成的/导出的独立文档、邮件发送的 HTML 文件时才使用内联注释。支持的内联标记impeccable-disable rule整文件impeccable-disable-line/impeccable-disable-next-line单行任意注释语法均可冒号或--后跟可选理由。检测器默认识别它们--no-inline-ignores或--no-config会绕过。完整命令示例值特定例外有把握的误报证据具名.impeccable/skills/impeccable/scripts/impeccable hooks ignore-value overused-font Inter --shared --reason User confirmed Inter is intentional自助例外字面意义动效证据具名.impeccable/skills/impeccable/scripts/impeccable hooks ignore-value bounce-easing bounce-ball --shared --reason Agent: literal ball-bounce animation, bounce easing is the subject整规则字体例外用户要求.impeccable/skills/impeccable/scripts/impeccable hooks ignore-rule overused-font --all-values --reason User asked to ignore overused fonts generally单规则单文件例外文件仍值得做其他审查.impeccable/skills/impeccable/scripts/impeccable hooks ignore-value design-system-font-size * --file src/overlay/widget.js --reason Injected widget builds its own type scale; DESIGN.mds ramp describes the site整文件例外文件完全超出范围.impeccable/skills/impeccable/scripts/impeccable hooks ignore-file src/legacy/Card.tsx约束Constraints绝不允许从这个命令手动修改.impeccable/config.json或.impeccable/config.local.json。一切写入都走impeccable hooks保证写入经过校验、文件形状保持一致。唯一例外detector.extensions没有 admin 动作用户要求覆盖某个模板栈时直接编辑 config.json 中的这一个字段其余字段保持不动不要从本流程编辑impeccable hook/impeccable hook-before-edit背后的 launcher 或二进制——那是技能管道skill plumbingCursor 会在检测到真实问题时阻止一次拟写入Claude Code、Codex、GitHub Copilot 不阻止编辑改为发出事后提醒。禁用 Hook 会同时停止阻止与提醒Hook 随 Impeccable 技能捆绑通过项目本地 manifest 安装.claude/settings.local.json、.codex/hooks.json、.cursor/hooks.json、.github/hooks/impeccable.jsonCodex 首次使用需经/hooks批准Cursor 需在 Settings - Hooks 确认启用GitHub Copilot CLI 在.github/hooks/impeccable.json提交到仓库默认分支后加载云 Agent 直接从仓库读取。失败模式Failure Modes若.impeccable/config.json或config.local.json不可读或格式损坏malformedHook 忽略该文件使用其余有效配置/默认值impeccable hooks status会把损坏文件标注为(malformed; ignored)实现见 crates/hook/src/admin.rs。若用户要求全局禁用 Hook先执行/impeccable hooks off对当前项目持久化写入hook.enabled: falselegacy 环境变量IMPECCABLE_HOOK_DISABLED1也可作为跟随 shell 的一次性覆盖。扩展阅读更深一层的实现细节会话去重缓存.impeccable/hook.cache.json记录每个会话最多 8 个会话下每个文件的 editCount 与已报告 findings同一 finding 不会重复轰炸 Agent。同一文件在一次会话内编辑超过EDIT_COUNT_THRESHOLD6 次后进入抑制suppressed状态输出抑制通知见 crates/hook/src/hook_lib.rs。Stop 通道最多扫描STOP_MAX_FILES20个文件crates/hook/src/hook_lib.rs。Stop 归属判定stop baselinestop_baseline.rs只在工具结果里有**已验证的首次 Edit/Write 前像preimage**时才建立基线并只用于纯文本检测器DOM 与设计系统 findings 依赖其他文件一律标为[attribution unknown]见 crates/hook/src/stop_baseline.rs。这会直接影响 Stop 深度通道的输出标注[new]表示会话新增的债务[attribution unknown]表示可能早于本会话。Cursor 防循环机制hook-before-edit对同一文件 同一 finding 签名累计cursorDenials超过 6 次后放行写入但附警告to avoid a loop见 crates/hook/src/before_edit.rs。安全与生成物过滤Hook 会跳过敏感路径.env、.git、密钥/凭据文件等与生成路径node_modules、dist/build/out/.next、.min.*、.d.ts、lock 文件等正则见 crates/hook/src/hook_lib.rs。结语Hook 与技能的互补最后回到文档的开篇立场每个 Hook 都是机械检查扫描器永远抓不到的反射式reflex准则沉淀在 craft-floor.md 中——该文件由技能在编辑 UI 之前加载因此无论是否接入 Hook 都生效。而没有自动 Hook 的会话会从impeccable context收到一条MANUAL_DETECTOR_REQUIRED指令要求会话结束时手动跑一次检测器。也就是说Hook 解决的是编辑当下的即时反馈与会话收尾的债务清点而 craft-floor 解决的是编辑之前的准则内化。三者逐次编辑层、Stop 深度层、craft-floor 准则合起来构成了 Impeccable 对 AI 编码助手设计质量的三重保障。【免费下载链接】impeccableThe design language that makes your AI harness better at design.项目地址: https://gitcode.com/GitHub_Trending/im/impeccable创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考