Problem
On Cursor, devkit guards shell commands but not file edits. An edit from the Cursor agent never claims a lock, so it can overwrite a file another session holds, and nothing warns either side.
Cursor has no event that fires before an edit. afterFileEdit fires once the write has happened, too late to claim or deny. The only way in is the generic preToolUse hook.
hooks/hooks-cursor.json leaves preToolUse unwired on purpose (#78). Three things about it are unconfirmed:
- Does empty stdout mean allow? Cursor's docs say a response that does not match the schema blocks the action, and don't say what an empty response is. devkit allows by printing nothing. If Cursor reads that as a block, every edit fails. Silence is only known to allow on
beforeShellExecution.
- What are Cursor's edit tool names? A matcher needs them. With a wrong matcher the hook never fires and edits stay unguarded, with no error.
- What does Cursor's edit payload look like?
src/bin/devkit/hook/edit.rs has no Cursor handling and has never seen a Cursor edit payload. If it cannot find a target, the edit path denies the edit.
Plan
- Capture a real payload. Point a throwaway
preToolUse hook at a script that copies stdin to a file and prints nothing. Ask the Cursor agent to create, edit and delete a file. If those succeed, question 1 is answered, and the captured payloads show the tool names and the file path key.
- Wire the hook. Add
preToolUse to hooks/hooks-cursor.json, running devkit hook pre-tool-use --harness cursor, with a matcher naming only the edit tools. Shell commands stay on beforeShellExecution, so the matcher must not include Shell, or every command would be guarded twice.
- Parse Cursor edits. Teach
edit.rs to read Cursor's payload: target path, session identity (Cursor's conversation_id, as the shell path already does in harness.rs) and cwd. Answer a denial in the envelope Cursor's preToolUse reads.
- Let Cursor claim. Check where the lock path excludes
Harness::Cursor, and lift that for edits. The shell write stage also excludes Cursor (writes_on in hook/shell.rs). That can stay a separate change.
- Update docs. Fill in the Cursor column in
docs/agents.md and drop the note that Cursor edits are not guarded.
If step 1 shows that empty stdout blocks, stop and decide whether to print an explicit allow. First confirm that an explicit allow does not skip Cursor's own approval prompt. If it does, devkit would approve edits the user would otherwise be asked about.
Tests
- A manifest test in
tests/hook_manifests.rs: Cursor's preToolUse has a matcher, and it does not match Shell.
- End-to-end tests that send captured Cursor edit payloads through
devkit hook pre-tool-use --harness cursor:
- an unheld file is allowed, and the claim lands
- a file another session holds is denied, in Cursor's envelope
- a payload with no target fails closed
sessionEnd, already wired for Cursor, releases the claims made during the session.
Problem
On Cursor, devkit guards shell commands but not file edits. An edit from the Cursor agent never claims a lock, so it can overwrite a file another session holds, and nothing warns either side.
Cursor has no event that fires before an edit.
afterFileEditfires once the write has happened, too late to claim or deny. The only way in is the genericpreToolUsehook.hooks/hooks-cursor.jsonleavespreToolUseunwired on purpose (#78). Three things about it are unconfirmed:beforeShellExecution.src/bin/devkit/hook/edit.rshas no Cursor handling and has never seen a Cursor edit payload. If it cannot find a target, the edit path denies the edit.Plan
preToolUsehook at a script that copies stdin to a file and prints nothing. Ask the Cursor agent to create, edit and delete a file. If those succeed, question 1 is answered, and the captured payloads show the tool names and the file path key.preToolUsetohooks/hooks-cursor.json, runningdevkit hook pre-tool-use --harness cursor, with a matcher naming only the edit tools. Shell commands stay onbeforeShellExecution, so the matcher must not includeShell, or every command would be guarded twice.edit.rsto read Cursor's payload: target path, session identity (Cursor'sconversation_id, as the shell path already does inharness.rs) andcwd. Answer a denial in the envelope Cursor'spreToolUsereads.Harness::Cursor, and lift that for edits. The shell write stage also excludes Cursor (writes_oninhook/shell.rs). That can stay a separate change.docs/agents.mdand drop the note that Cursor edits are not guarded.If step 1 shows that empty stdout blocks, stop and decide whether to print an explicit allow. First confirm that an explicit
allowdoes not skip Cursor's own approval prompt. If it does, devkit would approve edits the user would otherwise be asked about.Tests
tests/hook_manifests.rs: Cursor'spreToolUsehas a matcher, and it does not matchShell.devkit hook pre-tool-use --harness cursor:sessionEnd, already wired for Cursor, releases the claims made during the session.