feat(trace): install the Cursor shell hooks
The Cursor trace provider installed only the sessionStart, sessionEnd and afterFileEdit hooks, so the trace did not see the shell commands that the Cursor agent runs. Cursor has preToolUse and postToolUse hooks that cover its Shell tool, with the command, the working directory and a per-call tool_use_id.
This change:
- Installs
preToolUseandpostToolUsewith the matcherShellin.cursor/hooks.json.chainloop trace runinstalls them too. - Makes Cursor a command-tool provider. The shell hooks use the shared worktree snapshot path, so the files that a shell command changes are attributed to the AI, the same as for Claude Code and OpenCode.
ReadHookInputreadstool_name,tool_use_id,tool_input.commandandcwd. The transcript location stays on the first workspace root, because the shellcwdcan be a subdirectory. - Adds the
HookInput.Commandfield, with the same definition as in #3623, which reads it to find other checkouts. - Makes the pre hook always write
{"permission":"allow"}before it does any work. Cursor blocks the action when apreToolUseresponse is not valid JSON, and the trace must never block the agent. - Updates the trace README: shell changes attribution is now Partial for Cursor (the hooks are not verified for the Cursor CLI), and the hook table lists the new hooks.
Out of scope: the session link after push (the postToolUse response has no field that Cursor shows to the user), prompt hooks, and the Cursor CLI.
Tested with unit tests for hook install, upgrade of an existing entry without a matcher, payload parsing and the allow response, and with a handler test that drives the pre and post hooks with Cursor payloads and checks the attribution of a file that a shell command created.
AI disclosure: this change was made with Claude Code.
🤖 Opened by the eng-autopilot skill (Claude Code) for @jiparis