Hooks & Automation
.cursor/hooks.json, pre-edit and post-response hooks, auto-formatting, and skills that fire themselves
Hooks are the bits of Cursor that do work without you asking. Format on save. Auto-activate a skill when you open a .agent file. Run sf-lwc lint before a deploy. Once you set them up for a project, they run invisibly and the whole team benefits.

Before this page
If you haven't yet read about rules in Rules & AGENTS.md, start there. Rules are the "always-on nudge" layer. Hooks are the "actually run code" layer.
What hooks are
A hook is a shell command (or a reference to one) that Cursor runs at a specific event. Cursor supports a handful of event types:
- Pre-edit. Runs before an agent modifies a file. Useful for backups or validation.
- Post-edit. Runs after a file is written. Useful for formatting.
- Pre-response. Runs before the agent responds. Useful for injecting context.
- Post-response. Runs after the agent responds. Useful for running tests or deploys.
- On-save. Runs when a file is saved in the editor, including saves triggered by the agent.
Hooks live in .cursor/hooks.json in your project root. The file is a list of event/command pairs.
A minimal hooks.json
{
"hooks": [
{
"event": "post-edit",
"matcher": "**/*.cls",
"command": "npx prettier-plugin-apex --write ${file}"
},
{
"event": "post-edit",
"matcher": "**/lwc/**/*.{js,html,css}",
"command": "npx prettier --write ${file}"
}
]
}After the agent writes an Apex class or an LWC file, Prettier runs and cleans up the formatting. You don't have to remember to format. The agent's diffs are always tidy.
Setting up the basics
These are the three hooks every Salesforce project benefits from. Add them first.
1. Prettier on Apex and LWC
{
"event": "post-edit",
"matcher": "**/*.{cls,trigger,js,html,css}",
"command": "npx prettier --write ${file}"
}Install the plugin once per project:
npm install --save-dev prettier prettier-plugin-apex2. Lint LWCs after an edit
{
"event": "post-edit",
"matcher": "**/lwc/**/*.{js,html}",
"command": "npx eslint ${file}"
}If eslint finds problems, the agent sees them in its own output and can fix them next turn.
3. Validate metadata on save
{
"event": "on-save",
"matcher": "**/*-meta.xml",
"command": "xmllint --noout ${file}"
}Catches malformed XML before you try to deploy it.
Scoping with matcher
Most hook bugs are "the hook is running on files I didn't expect." Use glob matchers tightly.
**/*.cls. Every Apex class anywhere in the project.**/lwc/**/*.js. Only JS files inside LWC folders.force-app/main/default/permissionsets/*.xml. Only permission set XML, nothing else.
If a hook is firing on the wrong files, your matcher is too broad. Cursor shows which hook fired in the agent output for debugging.
Running something after every agent response
Post-response hooks are where the interesting automation lives. An example: run the Apex test suite after any edit to a class in classes/.
{
"event": "post-response",
"matcher": "conversation.touched-files.matches(/classes\\/.*\\.cls$/)",
"command": "sf apex run test --test-level RunLocalTests --wait 10 --target-org my-scratch"
}The matcher here isn't just a glob. Cursor also supports a small expression language for checking what happened in the conversation. Check the exact syntax against the Cursor release you're on, since it evolves.
Once set up, every time the agent edits an Apex class, the test suite runs and the results come back into the agent's next turn. The agent sees failures, fixes them, reruns. A real feedback loop without you touching the keyboard.
Skill auto-activation
You can also use hooks to nudge the agent into the right skill when it's about to do a specific kind of work. This one fires a reminder when a .agent file is opened:
{
"event": "pre-response",
"matcher": "conversation.open-files.matches(/\\.agent$/)",
"command": "echo 'The user is working with an Agent Script .agent file. Prefer the sf-ai-agentscript skill.'"
}The command output is appended to the agent's context. It sees the reminder and picks the right skill.
Environment variables and secrets
Hooks run with your shell environment. If you need an API key for the command, put it in .env, source it at the start of the command, or have a wrapper script do it. Never hard-code secrets into hooks.json. The file is checked into git.
{
"event": "post-response",
"matcher": "**/*.md",
"command": "./scripts/publish-doc.sh ${file}"
}publish-doc.sh sources .env at its top and does the work.
When hooks go wrong
The hook runs but nothing happens
Check out and err in the agent's output panel. Cursor shows you what each hook returned. A silent hook is usually exit code 0 with no output. Often a matcher mismatch.
The hook runs twice
Two hooks matched the same file. Tighten one of the matchers.
The hook is too slow
Post-edit hooks block the agent's next action. If your hook takes 30 seconds (full test suite, full deploy), move it to a post-response hook so it runs once per turn instead of once per file.
The hook modifies the file and creates a loop
If your hook writes to the same file it just edited, Cursor detects the write and can trigger the hook again. Prettier and ESLint handle this correctly. Custom scripts should check whether the change is a no-op before writing.
A realistic project hooks.json
Pulling the pieces above into one file:
{
"hooks": [
{
"event": "post-edit",
"matcher": "**/*.{cls,trigger}",
"command": "npx prettier --write ${file}"
},
{
"event": "post-edit",
"matcher": "**/lwc/**/*.{js,html,css}",
"command": "npx prettier --write ${file}"
},
{
"event": "post-edit",
"matcher": "**/lwc/**/*.js",
"command": "npx eslint --fix ${file}"
},
{
"event": "on-save",
"matcher": "**/*-meta.xml",
"command": "xmllint --noout ${file}"
},
{
"event": "pre-response",
"matcher": "conversation.open-files.matches(/\\.agent$/)",
"command": "echo 'A .agent file is open. Prefer sf-ai-agentscript for edits.'"
}
]
}Drop this in .cursor/hooks.json, install the npm deps, and commit. Every teammate who opens the project inherits the automation.
Verification
After you add hooks, ask the agent to make a small edit to a file that matches the hook:
Add a one-line comment to the top of any Apex class. I want to see the
hook fire.Watch the agent's output for the hook command. If it ran and exited 0, you're set. If it didn't run, your matcher isn't matching. If it ran but failed, you see the error in the output.
Next moves
- Subagents & Parallelism for the heavier automation that hooks can kick off.
- Demo Reset & Repeatability for the scripts you'll wire into pre- and post-response hooks.
- Rules & AGENTS.md if you want the lighter-weight "always remind the agent to do X" version.