Skip to content
Ultracite
Esc
↑↓navigate↵open⌘Jpreview
On this page

Agent Hooks

Run Ultracite after AI-assisted file edits with agent hooks so formatting and safe fixes happen automatically before review.

Ultracite can automatically format and fix code after AI coding assistants make edits through hooks. This ensures all AI-generated code adheres to your project’s standards without manual intervention.

The hooks work alongside your linter’s formatting. After an AI coding assistant edits a file, the hook automatically runs the project’s installed ultracite fix to format and fix auto-fixable linting issues, and in most hosts hands whatever it can’t fix back to the agent. This dual approach ensures both intelligent code generation (through AI rules) and automated code quality (through hooks).

Setup

Some tools like Cursor, Devin Desktop (formerly Windsurf), CodeBuddy, Claude Code, Codex, Deep Agents, GitHub Copilot, and Grok Build support hooks that run after AI agents edit files. Ultracite can configure these hooks automatically.

Automatic Setup

When you run npx ultracite init, you’ll be prompted to select which agent hooks you want to enable:

? Which agent hooks do you want to enable (optional)?
  ◯ Cursor
  ◯ Devin Desktop
  ◯ CodeBuddy
  ◯ Claude Code
  ◯ Codex
  ◯ Deep Agents
  ◯ GitHub Copilot
  ◯ Grok Build

This will create the relevant hook configuration file in your project directory.

Ultracite writes the following files:

  • Cursor: .cursor/hooks.json
  • Devin Desktop: .devin/hooks.v1.json
  • CodeBuddy: .codebuddy/settings.json
  • Claude Code: .claude/settings.json
  • Codex: .codex/hooks.json
  • Deep Agents: .deepagents/hooks.json
  • GitHub Copilot: .github/hooks/ultracite.json
  • Grok Build: .grok/hooks/ultracite.json

Devin Desktop, formerly Windsurf, runs Devin Local, its only agent since 3.9.19, which reads hooks in Claude Code’s format from .devin/hooks.v1.json (as Devin CLI does). Earlier versions wrote a post_write_code hook for Cascade, the agent Devin Local replaced, to .devin/hooks.json, and before that to .windsurf/hooks.json under the windsurf ID, which --hooks still accepts. Re-running init takes Ultracite’s hook out of both and leaves your own hooks there, or removes a file left empty.

Grok Build uses Claude Code’s format and maps its Write|Edit matcher onto its own file tools. It runs project hooks once you trust them with /hooks-trust.

Codex uses Claude Code’s format too, with a matcher on apply_patch, the tool it edits files with; the hook reads every file the patch adds, updates or moves. Codex loads a project’s .codex/hooks.json only once you trust the project, and runs the hook only after you review it in /hooks. Trust is tied to the hook’s content, so when re-running init changes the command, Codex asks again.

Deep Agents Code (dcode) also uses Claude Code’s format, and names its file tools Write and Edit in hook payloads. It runs a project’s hooks once you trust the workspace; a headless run needs --trust-project-hooks.

Hosts That Read Each Other’s Hooks

Some agents also run hooks written for another tool, so a project with several Ultracite hooks can run ultracite fix more than once per edit. The second run finds nothing left to fix, so it only costs time.

  • Grok Build also reads .claude/settings.json and .cursor/hooks.json, but runs a hook once when several files give it the same event, command and matcher. The Claude Code and Grok Build hooks match on all three, so together they run once. The Cursor hook has no matcher, so with it as well, Grok runs the fix twice.
  • The Copilot CLI also reads .claude/settings.json and runs the hooks from every file, so with both the Claude Code and GitHub Copilot hooks it runs the fix twice. The Copilot cloud agent reads only .github/hooks/.

The GitHub Copilot file uses the format the Copilot CLI and cloud agent read (a numeric version and camelCase events), which VS Code also understands:

{
  "hooks": {
    "postToolUse": [
      {
        "command": "npx ultracite fix --hook",
        "type": "command"
      }
    ]
  },
  "version": 1
}

Manual Setup

If you want to manually set things up or add hooks after initialization, run:

npx ultracite init --hooks cursor devin codebuddy claude codex deepagents copilot grok

This keeps the project’s current linter and your changes to its configs. Alternatively, you can manually create or edit the hook configuration files in your project directory and add the appropriate hook command.

How It Works

  1. Automatic Execution: After the AI agent edits a file, the hook runs ultracite fix --hook from the project’s own dependencies, through the package manager’s local runner (npx, pnpm exec, bunx and so on). It doesn’t go through the fix script, which may run another tool in a project that already had one. So that it doesn’t remove an import the agent has added but not used yet, the hook skips the unused-import fix: Biome projects pass --skip=correctness/noUnusedImports, and on ESLint projects fix --hook turns unused-imports/no-unused-imports off. Oxlint’s --fix doesn’t remove unused imports.
  2. Only the Edited File: --hook reads the agent’s hook payload from stdin and fixes just the file it names, so an edit costs one file instead of the whole project. Claude Code, CodeBuddy, Cursor, Devin Desktop, Deep Agents, GitHub Copilot in VS Code, the Copilot CLI, and Grok Build all send the path, and Codex sends the patch it applied; multi-file edits (VS Code’s multi-replace and apply_patch) fix each file they name, and a patch that only deletes files fixes nothing. VS Code and the Copilot CLI run the hook after every tool, so a tool that doesn’t edit files (like reading one) fixes nothing. A file outside the project, one that no longer exists, or one none of your linters handle (a Dockerfile, .env) is skipped. When no path can be read from the payload, the hook fixes the whole project as before. Re-running npx ultracite init --hooks rewrites a hook from an earlier init, including one generated before --hook existed or for a different package manager or linter.
  3. Code Formatting: Ultracite formats the edited code according to your linter configuration
  4. Linting Fixes: Auto-fixable linting issues are resolved automatically
  5. Remaining Problems Go Back to the Agent: When issues remain that need a manual fix, the hook prints them to stderr and exits with code 2. Claude Code, CodeBuddy, Codex, Deep Agents, Grok Build, and VS Code’s Local agent show a hook’s stderr to the agent on that exit code, so the agent sees what’s left and fixes it in its next step. Devin reads exit code 2 as “block”, so there the hook exits with 0 and prints what’s left as the hook’s additionalContext, which Devin adds to the agent’s context. Codex shows it in place of the patch’s own result, though the edit has already been applied. Cursor’s afterFileEdit hook doesn’t pass a hook’s output to the agent, and the Copilot CLI, the Copilot cloud agent and Copilot sessions on VS Code’s Agent Host show it only to you. There the hook keeps the linter’s own output and exit code.
  6. Seamless Integration: The process happens transparently without interrupting your workflow

Benefits

Using Ultracite agent hooks provides several advantages:

  • Consistency: All AI-generated code is automatically formatted to match your style
  • Quality: Linting issues are fixed immediately after code generation
  • Efficiency: No need to manually run formatters or linters after AI edits
  • Standards: Ensures AI-generated code always adheres to project standards
  • Clean Commits: Code is properly formatted before you even review it

Customization

Ultracite intelligently merges with existing hook configurations:

  • Preserves existing hooks: All existing hooks and their commands remain intact
  • Avoids duplicates: If a hook command already runs Ultracite, it won’t be added again. Only hook commands are checked, so a settings file that mentions Ultracite elsewhere (in a permission rule, say) still gets the hook
  • Upgrades old hooks: Re-running init replaces a hook command written by an earlier version (including the fix script commands written by v7 and earlier, and GitHub Copilot’s old PostToolUse format) instead of adding a second hook next to it
  • Non-destructive: Your existing hook configuration structure is maintained

You can also customize the hook behavior by modifying the hook configuration files directly if you need different formatting commands or additional post-edit actions.

Last updated on

Was this page helpful?