Cursor Reference
Cursor integrates with Cupcake through global hooks configured in ~/.cursor/hooks.json. Unlike Claude Code, Cursor only supports global hooks - not project-level configuration.
Supported Events
Cursor supports 6 hook events:
| Event | Description | Response Schema |
|---|---|---|
beforeShellExecution |
Before shell command | Full permission model |
beforeMCPExecution |
Before MCP tool | Full permission model |
beforeReadFile |
Before file read | Minimal (permission only) |
afterFileEdit |
After file edited | Fire-and-forget |
beforeSubmitPrompt |
Before prompt submit | Continue only |
stop |
Agent loop ends | Fire-and-forget |
Important: Cursor's beforeSubmitPrompt does NOT support context injection.
Event Fields
Common Fields
All Cursor events include:
{
"hook_event_name": "beforeShellExecution",
"conversation_id": "conv-123",
"generation_id": "gen-456",
"workspace_roots": ["/path/to/project"]
}
beforeShellExecution
{
"hook_event_name": "beforeShellExecution",
"conversation_id": "conv-123",
"generation_id": "gen-456",
"workspace_roots": ["/path/to/project"],
"command": "npm install express",
"cwd": "/path/to/project"
}
beforeMCPExecution
{
"hook_event_name": "beforeMCPExecution",
"conversation_id": "conv-123",
"generation_id": "gen-456",
"workspace_roots": ["/path/to/project"],
"tool_name": "database_query",
"tool_input": {
"query": "SELECT * FROM users"
},
"url": "http://localhost:3000",
"command": "npx mcp-server"
}
beforeReadFile
{
"hook_event_name": "beforeReadFile",
"conversation_id": "conv-123",
"generation_id": "gen-456",
"workspace_roots": ["/path/to/project"],
"file_path": "/path/to/project/secrets.env",
"content": "API_KEY=...",
"attachments": [
{
"type": "file",
"file_path": "/path/to/project/.cursorrules"
}
]
}
afterFileEdit
{
"hook_event_name": "afterFileEdit",
"conversation_id": "conv-123",
"generation_id": "gen-456",
"workspace_roots": ["/path/to/project"],
"file_path": "/path/to/project/src/main.ts",
"edits": [
{
"old_string": "const foo = 1",
"new_string": "const foo = 2"
}
]
}
beforeSubmitPrompt
{
"hook_event_name": "beforeSubmitPrompt",
"conversation_id": "conv-123",
"generation_id": "gen-456",
"workspace_roots": ["/path/to/project"],
"prompt": "Fix the bug in main.ts",
"attachments": [
{
"type": "rule",
"file_path": "/path/to/project/.cursorrules"
}
]
}
stop
{
"hook_event_name": "stop",
"conversation_id": "conv-123",
"generation_id": "gen-456",
"workspace_roots": ["/path/to/project"],
"status": "completed"
}
Status values: completed, aborted, error
Response Formats
Full Permission Model
Used by beforeShellExecution and beforeMCPExecution:
Allow:
{
"permission": "allow"
}
Deny:
{
"permission": "deny",
"userMessage": "This command is not allowed",
"agentMessage": "Policy blocked: dangerous command pattern detected"
}
Ask (prompt user):
{
"permission": "ask",
"question": "This command modifies system files. Continue?",
"userMessage": "System modification detected",
"agentMessage": "Awaiting user confirmation for system modification"
}
Minimal Schema
Used by beforeReadFile:
Allow:
{
"permission": "allow"
}
Deny:
{
"permission": "deny"
}
Note: beforeReadFile does not support userMessage or agentMessage.
Continue Only
Used by beforeSubmitPrompt:
Allow:
{
"continue": true
}
Block:
{
"continue": false
}
Note: beforeSubmitPrompt only supports a boolean continue field. Context injection is NOT supported.
Fire-and-Forget
Used by afterFileEdit and stop:
{}
These events don't expect a response that affects agent behavior.
Hook Configuration
The cupcake init --harness cursor command configures hooks in ~/.cursor/hooks.json:
{
"version": 1,
"hooks": {
"beforeShellExecution": [
{
"command": "cupcake eval --harness cursor --policy-dir .cupcake"
}
],
"beforeMCPExecution": [
{
"command": "cupcake eval --harness cursor --policy-dir .cupcake"
}
],
"afterFileEdit": [
{
"command": "cupcake eval --harness cursor --policy-dir .cupcake"
}
],
"beforeReadFile": [
{
"command": "cupcake eval --harness cursor --policy-dir .cupcake"
}
],
"beforeSubmitPrompt": [
{
"command": "cupcake eval --harness cursor --policy-dir .cupcake"
}
],
"stop": [
{
"command": "cupcake eval --harness cursor --policy-dir .cupcake"
}
]
}
}
Note: Cursor hooks use relative paths (.cupcake) which resolve to the current project directory. For global policies, use absolute paths.
Writing Policies
Basic Policy Structure
# METADATA
# scope: package
# custom:
# routing:
# required_events: ["beforeShellExecution"]
package cupcake.policies.cursor.shell_policy
import rego.v1
deny contains decision if {
input.hook_event_name == "beforeShellExecution"
contains(input.command, "rm -rf")
decision := {
"rule_id": "CURSOR-SAFETY-001",
"reason": "Destructive command blocked",
"severity": "CRITICAL"
}
}
Protecting Sensitive Files
# METADATA
# scope: package
# custom:
# routing:
# required_events: ["beforeReadFile"]
package cupcake.policies.cursor.protect_secrets
import rego.v1
deny contains decision if {
input.hook_event_name == "beforeReadFile"
endswith(input.file_path, ".env")
decision := {
"rule_id": "CURSOR-SECRET-001",
"reason": "Access to .env files is restricted",
"severity": "HIGH"
}
}
Post-Edit Validation
# METADATA
# scope: package
# custom:
# routing:
# required_events: ["afterFileEdit"]
# signals:
# - eslint-check
package cupcake.policies.cursor.post_edit_lint
import rego.v1
deny contains decision if {
input.hook_event_name == "afterFileEdit"
endswith(input.file_path, ".ts")
lint_result := input.signals.eslint_check
is_object(lint_result)
lint_result.exit_code != 0
decision := {
"rule_id": "CURSOR-LINT-001",
"reason": concat("", ["Linting failed: ", lint_result.output]),
"severity": "MEDIUM"
}
}
Key Differences from Claude Code
| Feature | Claude Code | Cursor |
|---|---|---|
| Hook location | Project or global | Global only (~/.cursor/) |
| Config file | .claude/settings.json |
~/.cursor/hooks.json |
| Config format | Complex with matcher, type |
Simple with just command |
| Context injection | Supported on prompts | Not supported |
| Response field | permissionDecision in hookSpecificOutput |
permission at top level |
| Event naming | PreToolUse, PostToolUse |
beforeShellExecution, afterFileEdit, etc. |
Resources
- Setup Guide - Installation and configuration
- Cursor Tutorial - Hands-on walkthrough