Skip to content

cursor: support Cursor CLI hook differences#522

Closed
squishykid wants to merge 4 commits intomainfrom
rwr/tests-for-cursor-agent-cli
Closed

cursor: support Cursor CLI hook differences#522
squishykid wants to merge 4 commits intomainfrom
rwr/tests-for-cursor-agent-cli

Conversation

@squishykid
Copy link
Member

Summary

  • Cursor CLI never provides transcript_path on stop/sessionEnd hooks, breaking the turn-end handler. Fixes by computing the path dynamically via GetSessionDir + ResolveSessionFile when transcript_path is null.
  • Updates GetSessionDir to include agent-transcripts/ in the path, matching the real filesystem layout.
  • Adds support for both flat (CLI: <id>.jsonl) and nested (IDE: <id>/<id>.jsonl) transcript layouts in ResolveSessionFile.
  • Enriches hook types (sessionStartRaw, stopHookInputRaw, sessionEndRaw) with fields present in both IDE and CLI payloads.

Test plan

  • mise run fmt && mise run lint && mise run test:ci all pass
  • Verify with real Cursor CLI hook payloads
  • Verify with real Cursor IDE hook payloads (backward compat)

🤖 Generated with Claude Code

Cursor CLI never provides transcript_path on stop/sessionEnd hooks,
breaking the turn-end handler. Fix by computing the path dynamically
via GetSessionDir + ResolveSessionFile when transcript_path is null.

Also updates GetSessionDir to include agent-transcripts/ in the path,
adds support for both flat (CLI) and nested (IDE) transcript layouts
in ResolveSessionFile, and enriches hook types with fields present in
both IDE and CLI payloads.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Entire-Checkpoint: b1ed1e053951
Copilot AI review requested due to automatic review settings February 26, 2026 13:15
@cursor
Copy link

cursor bot commented Feb 26, 2026

PR Summary

Medium Risk
Changes how Cursor transcript paths are resolved and how stop/sessionEnd hook payloads are parsed, which can affect session lifecycle handling if path computation is wrong. Risk is mitigated by added unit tests covering both IDE and CLI layouts.

Overview
Adds Cursor CLI compatibility by computing SessionRef dynamically for stop and sessionEnd hooks when transcript_path is missing, using repo root + GetSessionDir + ResolveSessionFile.

Updates transcript path resolution to match real Cursor storage: GetSessionDir now includes agent-transcripts/, and ResolveSessionFile supports both nested (IDE: <dir>/<id>/<id>.jsonl) and flat (CLI: <dir>/<id>.jsonl) layouts, preferring nested when present.

Refines Cursor hook payload structs (sessionStartRaw, stopHookInputRaw, sessionEndRaw) to reflect IDE vs CLI fields and expands tests to cover the new path/layout and no-transcript_path cases.

Written by Cursor Bugbot for commit b8914c6. Configure here.

@squishykid
Copy link
Member Author

cursor CLI log:

2026-02-26T13:26:45+01:00 [sessionStart] {"conversation_id":"723fa68d-2697-484c-a4b7-97b38ff84e8a",
"generation_id":"723fa68d-2697-484c-a4b7-97b38ff84e8a",
"model":"claude-4.6-opus-high-thinking",
"session_id":"723fa68d-2697-484c-a4b7-97b38ff84e8a",
"is_background_agent":false,"hook_event_name":"sessionStart",
"cursor_version":"2026.02.13-41ac335",
"workspace_roots":["/Users/robin/Developer/bingo"],"user_email":"robin@entire.io",
"transcript_path":null}
2026-02-26T13:26:54+01:00 [beforeSubmitPrompt] {"conversation_id":"723fa68d-2697-484c-a4b7-97b38ff84e8a",
"generation_id":"d81f6df0-1ed9-469b-b9f8-079e8f487699",
"model":"claude-4.6-opus-high-thinking",
"prompt":"hello please create boop.txt",
"attachments":[],"hook_event_name":"beforeSubmitPrompt",
"cursor_version":"2026.02.13-41ac335",
"workspace_roots":["/Users/robin/Developer/bingo"],"user_email":"robin@entire.io",
"transcript_path":null}
2026-02-26T13:27:02+01:00 [stop] {"conversation_id":"723fa68d-2697-484c-a4b7-97b38ff84e8a",
"generation_id":"d81f6df0-1ed9-469b-b9f8-079e8f487699",
"model":"claude-4.6-opus-high-thinking",
"status":"completed",
"loop_count":0,"hook_event_name":"stop",
"cursor_version":"2026.02.13-41ac335",
"workspace_roots":["/Users/robin/Developer/bingo"],"user_email":"robin@entire.io",
"transcript_path":null}
2026-02-26T13:27:13+01:00 [sessionEnd] {"conversation_id":"723fa68d-2697-484c-a4b7-97b38ff84e8a",
"generation_id":"723fa68d-2697-484c-a4b7-97b38ff84e8a",
"model":"claude-4.6-opus-high-thinking",
"session_id":"723fa68d-2697-484c-a4b7-97b38ff84e8a",
"reason":"completed",
"duration_ms":29478,"is_background_agent":false,"final_status":"completed",
"hook_event_name":"sessionEnd",
"cursor_version":"2026.02.13-41ac335",
"workspace_roots":["/Users/robin/Developer/bingo"],"user_email":"robin@entire.io",
"transcript_path":null}

@squishykid
Copy link
Member Author

cursor IDE log

2026-02-26T13:46:24+01:00 [sessionStart] {"conversation_id":"cd0ffdde-78ee-4a63-b0da-1c22088b7b59",
"generation_id":"",
"model":"default",
"session_id":"cd0ffdde-78ee-4a63-b0da-1c22088b7b59",
"is_background_agent":false,"composer_mode":"agent",
"hook_event_name":"sessionStart",
"cursor_version":"2.5.17",
"workspace_roots":["/Users/robin/Developer/bingo"],"user_email":"robin@entire.io",
"transcript_path":null}
2026-02-26T13:46:31+01:00 [beforeSubmitPrompt] {"conversation_id":"cd0ffdde-78ee-4a63-b0da-1c22088b7b59",
"generation_id":"ed905338-b0a6-40f3-a26e-3999e7694d81",
"model":"default",
"prompt":"create file b.txt",
"attachments":[],"hook_event_name":"beforeSubmitPrompt",
"cursor_version":"2.5.17",
"workspace_roots":["/Users/robin/Developer/bingo"],"user_email":"robin@entire.io",
"transcript_path":null}
2026-02-26T13:46:37+01:00 [stop] {"conversation_id":"cd0ffdde-78ee-4a63-b0da-1c22088b7b59",
"generation_id":"ed905338-b0a6-40f3-a26e-3999e7694d81",
"model":"default",
"status":"completed",
"loop_count":0,"hook_event_name":"stop",
"cursor_version":"2.5.17",
"workspace_roots":["/Users/robin/Developer/bingo"],"user_email":"robin@entire.io",
"transcript_path":"/Users/robin/.cursor/projects/Users-robin-Developer-bingo/agent-transcripts/cd0ffdde-78ee-4a63-b0da-1c22088b7b59/cd0ffdde-78ee-4a63-b0da-1c22088b7b59.jsonl"}
2026-02-26T13:47:03+01:00 [sessionEnd] {"conversation_id":"cd0ffdde-78ee-4a63-b0da-1c22088b7b59",
"generation_id":"ed905338-b0a6-40f3-a26e-3999e7694d81",
"model":"default",
"session_id":"cd0ffdde-78ee-4a63-b0da-1c22088b7b59",
"reason":"user_close",
"duration_ms":39100,"is_background_agent":false,"final_status":"completed",
"hook_event_name":"sessionEnd",
"cursor_version":"2.5.17",
"workspace_roots":["/Users/robin/Developer/bingo"],"user_email":"robin@entire.io",
"transcript_path":"/Users/robin/.cursor/projects/Users-robin-Developer-bingo/agent-transcripts/cd0ffdde-78ee-4a63-b0da-1c22088b7b59/cd0ffdde-78ee-4a63-b0da-1c22088b7b59.jsonl"}

@squishykid squishykid marked this pull request as ready for review February 26, 2026 13:18
@squishykid squishykid requested a review from a team as a code owner February 26, 2026 13:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Cursor CLI compatibility to the Cursor agent integration by handling missing transcript_path values and supporting both Cursor IDE and CLI transcript directory layouts.

Changes:

  • Computes transcript refs dynamically for stop and sessionEnd hooks when transcript_path is missing (Cursor CLI behavior).
  • Updates Cursor transcript directory resolution to include agent-transcripts/ and supports both flat and nested transcript layouts.
  • Expands Cursor hook raw payload types and adds/updates unit tests for these behaviors.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
cmd/entire/cli/agent/cursor/types.go Splits/extends raw hook payload structs to cover IDE vs CLI field differences.
cmd/entire/cli/agent/cursor/lifecycle.go Resolves missing transcript_path by deriving a transcript ref from repo/worktree + Cursor session dir logic.
cmd/entire/cli/agent/cursor/lifecycle_test.go Adds tests for CLI hooks missing transcript_path and ensures IDE-provided paths are preserved.
cmd/entire/cli/agent/cursor/cursor.go Updates session dir to include agent-transcripts/ and makes transcript file resolution prefer nested layout.
cmd/entire/cli/agent/cursor/cursor_test.go Adds coverage for flat vs nested transcript layout resolution and validates updated session dir suffix.

Comment on lines +150 to +153
// Set up a temp dir that simulates the Cursor project dir with a flat transcript
tmpDir := t.TempDir()
transcriptDir := filepath.Join(tmpDir, "agent-transcripts")
if err := os.MkdirAll(transcriptDir, 0o755); err != nil {
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: this comment says the temp dir simulates the Cursor project dir, but the env override is effectively the transcript/session dir (agent-transcripts). Consider rewording the comment to match what’s being overridden.

Copilot uses AI. Check for mistakes.
// We prefer the nested path if it exists, otherwise fall back to flat.
func (c *CursorAgent) ResolveSessionFile(sessionDir, agentSessionID string) string {
nested := filepath.Join(sessionDir, agentSessionID, agentSessionID+".jsonl")
if _, err := os.Stat(nested); err == nil {
Copy link

Copilot AI Feb 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ResolveSessionFile falls back to the flat path for any os.Stat error on the nested path. If the nested file exists but stat fails for a non-NotExist reason (e.g., permission denied, broken symlink), this will silently return a likely-wrong flat path and hide the real error. Consider only falling back when os.IsNotExist(err) is true; otherwise keep the nested path (or surface the error by changing the function signature).

Suggested change
if _, err := os.Stat(nested); err == nil {
if _, err := os.Stat(nested); err == nil || !os.IsNotExist(err) {

Copilot uses AI. Check for mistakes.
Entire-Checkpoint: ba0bcb8add65
Docs should say we support cursor ide and cursor cli
@squishykid squishykid marked this pull request as draft February 26, 2026 14:48
@squishykid
Copy link
Member Author

use #527 instead

@squishykid squishykid closed this Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants