6.8 KiB
/impeccable hooks
Manage the design detector hook for the current project.
The hook runs the impeccable design detector on direct file edits to design-relevant files (.tsx, .jsx, .html, .vue, .svelte, .astro, .css, .scss, .sass, .less, .ts, .js). Claude Code and Codex use PostToolUse and push a short system reminder into the agent's context after the edit; findings get a correction prompt, pending issues get a re-nudge, and clean UI-ish files get a short ack unless quiet mode is on (hook.quiet in config). Plain .ts and .js files are still scanned, but stay quiet unless the detector finds something. Cursor uses preToolUse to block bad proposed writes before they land and stays silent when it allows a clean write.
This command toggles the hook per project by editing .impeccable/config.json (the unified Impeccable config; hook settings live under its hook key). Per-developer overrides, including the install consent decision (hook.consent) the CLI records, live in the gitignored .impeccable/config.local.json. Set hook.enabled: false to turn the hook off, hook.quiet: true to silence the clean/pending acks, or hook.auditLog to a file path for an NDJSON log. The legacy IMPECCABLE_HOOK_DISABLED, IMPECCABLE_HOOK_QUIET, and IMPECCABLE_HOOK_LOG env vars are still honored and override these config values when set.
Supported harnesses: Claude Code (.claude/settings.local.json in the project, which is gitignored so the hook stays machine-local; a hook you move into the shared settings.json is honored in place too), Codex (.codex/hooks.json in the project), and Cursor (.cursor/hooks.json in the project).
On Cursor, preToolUse checks proposed Write/Edit/Shell write content and denies only when the real detector finds an issue. The denial message is visible to the agent as the tool error, so the agent can reconsider before the bad write lands.
Routing
The first argument is the action. Defaults to status.
| Action | What it does |
|---|---|
status |
Print current state, shared/local config paths, ignored rules / files / values, env override. |
on |
Set enabled: true in .impeccable/config.json, record local hook consent as accepted, and install/repair provider hook manifests when the skill is installed. |
off |
Set enabled: false in .impeccable/config.json. |
ignore-rule <id> |
Append <id> to ignoreRules; for overused-font, requires --all-values. |
ignore-file <glob> |
Append <glob> to ignoreFiles. |
ignore-value <id> <value> [--shared] [--reason "..."] |
Append a rule/value suppression to shared .impeccable/config.json. |
ignore-value <id> <value> --local [--reason "..."] |
Append a private rule/value suppression to .impeccable/config.local.json. |
reset |
Delete the project config, dedup cache, and Cursor pending queue. |
Flow
-
Resolve the action from the user's argument. If no action was given, default to
status. -
Invoke the admin script and pass the user's output through verbatim:
node {{scripts_path}}/hook-admin.mjs <action> [args...] -
If
<action>isoff, follow up with a one-line note: "Done. New edits will not trigger the design hook in this project until you run{{command_prefix}}impeccable hooks on." -
If
<action>ison, follow up with: "Done. The design hook will fire after the next Edit/Write/MultiEdit on a UI file." -
If
<action>isignore-value,ignore-file, orignore-rule, just print the script output. The default scope is shared.impeccable/config.json; add--localonly when the user explicitly asks for a private exception. -
If
<action>isstatus, just print the script output. Do not add commentary unless the user asked a follow-up question.
Intentional findings
The hook itself never writes ignore config. Persist an exception only after the user explicitly confirms the flagged issue is intentional, and always go through hook-admin.mjs.
Prefer the narrowest exception:
- If the finding line shows an exact
ignore-valuecommand, run that command. This writes shared.impeccable/config.jsonby default. - For value-specific findings such as
overused-fontandbounce-easing, useignore-valuewhen the user confirms the specific value. Do not useignore-rule overused-fontfor a specific font. - If the finding has no value-specific command, such as
side-tab, preferignore-file <path>for the current file. - Use
ignore-rule <id>only when the user asks to suppress that whole rule across the project. For broad overused-font suppression, useignore-rule overused-font --all-valuesonly when the user asks to ignore overused fonts generally. - Do not add source comments such as
impeccable: ignore; inline comments pollute code and are not a supported suppression mechanism.
Example value-specific exception:
node {{scripts_path}}/hook-admin.mjs ignore-value overused-font Inter --shared --reason "User confirmed Inter is intentional"
Example intentional motion exception:
node {{scripts_path}}/hook-admin.mjs ignore-value bounce-easing bounce-ball --shared --reason "User confirmed ball bounce animation is intentional"
Example whole-rule font exception:
node {{scripts_path}}/hook-admin.mjs ignore-rule overused-font --all-values --reason "User asked to ignore overused fonts generally"
Example file-scoped exception:
node {{scripts_path}}/hook-admin.mjs ignore-file "src/legacy/Card.tsx"
Constraints
- Never modify
.impeccable/config.jsonor.impeccable/config.local.jsonby hand from this command. Always go throughhook-admin.mjsso writes stay validated and the file shape stays consistent. - Do not edit the hook scripts themselves (
hook.mjs,hook-lib.mjs,hook-before-edit.mjs) from this flow. Those are skill plumbing. - Cursor can block a proposed write when the detector finds a real issue. Claude Code and Codex do not block the edit; they emit a post-edit reminder instead. Disabling stops both blocking and reminders.
- The hook is bundled with the Impeccable skill and installed through project-local manifests:
.claude/settings.local.json,.codex/hooks.json, and.cursor/hooks.json. On Codex, the user must approve the hook via/hooksthe first time. On Cursor, confirm hooks are enabled under Settings -> Hooks.
Failure modes
- If
.impeccable/config.jsonor.impeccable/config.local.jsonis unreadable or malformed, the hook ignores that file and uses the remaining valid config/defaults.hook-admin.mjs statuswill show malformed files as ignored. - If the user asks to "disable the hook" globally, lead with
{{command_prefix}}impeccable hooks off(persistent for this project; writeshook.enabled: falseto config). The legacyIMPECCABLE_HOOK_DISABLED=1env var also works as a one-shot override that follows the shell.