fix: align Cursor hook contract

This commit is contained in:
ZacharyZhang-NY committed 2026-07-19 23:16:22 -04:00
1 parent 16e7e7a9a6
commit 4f9108099c
4 files changed
+36 -4

No files matched your search

+1
View File
@@ -29,6 +29,7 @@ This repository publishes the open-source server, console, integration catalog,
- Write Auggie hooks only to `~/.augment/settings.json`; keep system and workspace settings read-only. Generate `.cmd` wrappers on Windows and `.sh` wrappers on Unix because Auggie dispatches supported script paths, and express hook timeouts in milliseconds. - Write Auggie hooks only to `~/.augment/settings.json`; keep system and workspace settings read-only. Generate `.cmd` wrappers on Windows and `.sh` wrappers on Unix because Auggie dispatches supported script paths, and express hook timeouts in milliseconds.
- Validate Auggie matcher syntax during installation with Node.js `new RegExp`; keep status and uninstall independent from the JavaScript validator so recovery remains available offline. - Validate Auggie matcher syntax during installation with Node.js `new RegExp`; keep status and uninstall independent from the JavaScript validator so recovery remains available offline.
- Write Cline file hooks to `$CLINE_DIR/hooks` with `~/.cline/hooks` as the default; keep Documents and workspace hook roots read-only. Use supported event filenames, preserve official hook input byte-for-byte, use `process.execPath` in Node and Go wrappers, and resolve Python runtimes from absolute shebangs. Translate guard exit code `2` into JSON stdout with `cancel: true`. Treat hook errors, timeouts, and invalid control JSON as fail-open. - Write Cline file hooks to `$CLINE_DIR/hooks` with `~/.cline/hooks` as the default; keep Documents and workspace hook roots read-only. Use supported event filenames, preserve official hook input byte-for-byte, use `process.execPath` in Node and Go wrappers, and resolve Python runtimes from absolute shebangs. Translate guard exit code `2` into JSON stdout with `cancel: true`. Treat hook errors, timeouts, and invalid control JSON as fail-open.
- Model Cursor as one CLI-and-IDE hook contract. Preserve user, project, and enterprise sources in documented priority order; forward `tool_name`, `tool_input`, and `conversation_id`; use exit code `2` for denial; record `failClosed` as the per-script failure-mode control.
- Preserve Factory Droid's scope-root `hooks.json` precedence, legacy nested hook source, and per-event `settings.json` fallback. Scope-root and legacy files store event keys at the document root; settings stores the same map under `hooks`. Edit only effective user sources; keep project and organization sources read-only. Preserve JSONC comments through syntax-tree edits, use SDK-native absolute runtime commands, commit generated runtime artifacts and hook sources as one recoverable transaction, and require `/hooks` review after external changes. - Preserve Factory Droid's scope-root `hooks.json` precedence, legacy nested hook source, and per-event `settings.json` fallback. Scope-root and legacy files store event keys at the document root; settings stores the same map under `hooks`. Edit only effective user sources; keep project and organization sources read-only. Preserve JSONC comments through syntax-tree edits, use SDK-native absolute runtime commands, commit generated runtime artifacts and hook sources as one recoverable transaction, and require `/hooks` review after external changes.
- Qwen Code `0.20.0` resolves user settings through explicit `QWEN_HOME`, then user-level `.qwen/.env`, then `~/.env`, with `~/.qwen/settings.json` as the default. Keep workspace settings read-only. Preserve JSON comments, reject trailing commas and duplicate keys before writes, express timeouts in milliseconds, propagate native exit code `2` through PowerShell, and require `/hooks` review. - Qwen Code `0.20.0` resolves user settings through explicit `QWEN_HOME`, then user-level `.qwen/.env`, then `~/.env`, with `~/.qwen/settings.json` as the default. Keep workspace settings read-only. Preserve JSON comments, reject trailing commas and duplicate keys before writes, express timeouts in milliseconds, propagate native exit code `2` through PowerShell, and require `/hooks` review.
- Commit Qwen Code runtime config, private key, audit runtime, and user settings in one rollback-capable transaction. Status requires enabled `PreToolUse` and `PostToolUse` hooks with matching managed runtime files. - Commit Qwen Code runtime config, private key, audit runtime, and user settings in one rollback-capable transaction. Status requires enabled `PreToolUse` and `PostToolUse` hooks with matching managed runtime files.
+2
View File
@@ -8,6 +8,8 @@ Grok Build adapters write native user hook files under `$GROK_HOME/hooks` or `~/
GitHub Copilot command `preToolUse` hooks deny on crashes and every non-zero exit. Command timeouts continue through the normal permission flow, so `timeout_failure_mode` records that explicit exception when it differs from `failure_mode`. GitHub Copilot command `preToolUse` hooks deny on crashes and every non-zero exit. Command timeouts continue through the normal permission flow, so `timeout_failure_mode` records that explicit exception when it differs from `failure_mode`.
Cursor uses the same `preToolUse` and `postToolUse` contract across its CLI and IDE. User, project, and enterprise hook sources merge by documented priority; command exit code `2` denies an action, while per-script `failClosed` controls crash, timeout, and invalid-output behavior.
`delivery_state` is computed from the `node`, `python`, and `go` adapter flags: `delivery_state` is computed from the `node`, `python`, and `go` adapter flags:
| State | Meaning | | State | Meaning |
+12 -4
View File
@@ -148,20 +148,28 @@
"id": "cursor", "id": "cursor",
"name": "Cursor", "name": "Cursor",
"vendor": "Anysphere", "vendor": "Anysphere",
"surface": "ide", "surface": "cli_and_ide",
"integration_mode": "command_hooks", "integration_mode": "command_hooks",
"config_format": "json", "config_format": "json",
"config_paths": [ "config_paths": [
"~/.cursor/hooks.json", "/Library/Application Support/Cursor/hooks.json",
".cursor/hooks.json" "/etc/cursor/hooks.json",
"C:\\ProgramData\\Cursor\\hooks.json",
".cursor/hooks.json",
"~/.cursor/hooks.json"
], ],
"events": { "events": {
"before_tool": "preToolUse", "before_tool": "preToolUse",
"after_tool": "postToolUse" "after_tool": "postToolUse"
}, },
"event_fields": {
"name": "tool_name",
"input": "tool_input",
"session": "conversation_id"
},
"blocking": { "blocking": {
"mechanism": "exit_code_2", "mechanism": "exit_code_2",
"failure_mode": "fail_open" "failure_mode": "configurable"
}, },
"adapters": { "node": true, "python": true, "go": true }, "adapters": { "node": true, "python": true, "go": true },
"delivery_state": "available", "delivery_state": "available",
+21
View File
@@ -296,6 +296,27 @@ test('high-drift providers retain their verified hook contracts', async () => {
failure_mode: 'fail_closed', failure_mode: 'fail_closed',
timeout_failure_mode: 'fail_open', timeout_failure_mode: 'fail_open',
}); });
assert.equal(providers.get('cursor').surface, 'cli_and_ide');
assert.deepEqual(providers.get('cursor').config_paths, [
'/Library/Application Support/Cursor/hooks.json',
'/etc/cursor/hooks.json',
'C:\\ProgramData\\Cursor\\hooks.json',
'.cursor/hooks.json',
'~/.cursor/hooks.json',
]);
assert.deepEqual(providers.get('cursor').events, {
before_tool: 'preToolUse',
after_tool: 'postToolUse',
});
assert.deepEqual(providers.get('cursor').event_fields, {
name: 'tool_name',
input: 'tool_input',
session: 'conversation_id',
});
assert.deepEqual(providers.get('cursor').blocking, {
mechanism: 'exit_code_2',
failure_mode: 'configurable',
});
assert.deepEqual(providers.get('droid').config_paths, [ assert.deepEqual(providers.get('droid').config_paths, [
'~/.factory/hooks.json', '~/.factory/hooks.json',
'~/.factory/settings.json', '~/.factory/settings.json',