Automate Cursor Code Quality with afterFileEdit

AI-generated code often misses your project's formatting and linting standards. You find yourself running manual fixes after every AI edit, breaking your flow and risking inconsistent code. Instead of treating formatting as a manual cleanup step, you can make it automatic.

This lesson shows you how to use the afterFileEdit hook to automatically run your linter or formatter—like Biome, Prettier, or ESLint—every time the AI modifies a file. The hook detects which file was changed, checks if it matches your criteria (e.g., TypeScript files), and executes your formatting tool on it. Code quality standards are enforced instantly and automatically, with zero manual intervention.

Hook Configuration

First, you need to tell Cursor which script to run after a file is edited. This is done in the .cursor/hooks.json file. Here, we define an afterFileEdit hook that executes our TypeScript script using Bun.

{ "version": 1, "hooks": { "afterFileEdit": [ { "command": "bun run hooks/after-file-edit.ts" } ] } }

Implementing the Hook Logic

The hook receives a payload with information about the edited file. You parse this to get the file path, check if it matches your criteria (like a .ts extension), and if so, run your formatting tool on it.

Bun's Bun.$ syntax makes running shell commands from TypeScript elegant and safe—perfect for executing your linter or formatter.

import type { AfterFileEditPayload } from "cursor-hooks"; // Read the payload from standard input const input: AfterFileEditPayload = await Bun.stdin.json(); // Check if the edited file is a TypeScript file if (input.file_path.endsWith(".ts")) { // Run the Biome linter/formatter on the file const result = await Bun.$`bunx @biomejs/biome lint --fix --unsafe ${input.file_path}`; // Log the output for debugging purposes console.log(JSON.stringify(result.stdout, null, 2)); }

Seeing it in Action

Ask the AI to make a code change:

add a const foo = "bar"

The moment the AI applies the edit, your hook triggers automatically in the background. The formatter runs, applies your project's style rules, and the code is cleaned up—all without you doing anything. Your code stays consistent and follows your standards automatically.

Terminal Commands

Initialize a new Bun project in your workspace root.

bun init

This is the underlying command our hook script runs to format a specific file.

bunx @biomejs/biome lint --fix --unsafe index.ts
Advanced Cursor Hooks
Lesson 6 of 8

Advanced Cursor Hooks

Learn to build type-safe Cursor hooks with TypeScript & Bun. Automate AI-assisted coding, enforce standards, and streamline your development workflow.

Share with a coworker

Transcript

[00:00] Now we're going to bun init in the root of the project and set up a blank project so that we have an index.ts file. Then we'll set up a hook for after file edit. Let tab complete the command and then create our after file edit file. After file edit hook here. And bring in all of the proper types.

[00:25] After file edit payload and after file edit response. Hit tab to replace all of these. So now we have our template set up for what to run after any time a file is edited. So the scenario we're going to set up is if the input file path ends with TypeScript, then we're going to run a formatting command. And we're going to do that in bun with bun$ and then backticks here.

[00:51] And we'll just use a biome command of bunx at biome.js, biome, and lint, and fix, and unsafe. And we'll target that input file path. Then we'll await this and get back the result from this. And then according to the docs the after file edit is just void, so we really don't get a chance to say anything back to the agent. We just get to run this command, and I'm just going to console.log the JSON stringify result of the standard out, just so that at least shows up in my hooks log.

[01:32] So now if I look at my index.ts, open an agent and say add a const foo equals bar. Then we'll also watch the output to see this happen. Let's clear this out. Hit enter here. You'll see that it added const foo equals bar, and then our formatting tool for after file edit executed our hook.

[01:58] We got the original content and then the new content. And you'll see the new content we got did not have this underscore. But if you run biome on that file, let's try it again, we'll keep this, remove this, you'll see that if we run our after file edit command here, we'll just paste it down here, and point this to the index.ts, go back to our index, you'll see once we hit enter that it changed it from normal foo to underscore foo.