Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eae8dfc

Browse files
mksgluclaude
andcommitted
fix(cursor): use verified env vars, remove broken sessionStart hook
Based on source-verified audit against Cursor forum, GitButler deep dive, and macos-notify-mcp: - Detection: replace fabricated CURSOR_CWD/CURSOR_SESSION_ID with verified CURSOR_TRACE_ID (MCP context) and CURSOR_CLI (terminal context) - sessionStart: set capabilities.sessionStart=false (Cursor rejects it with "Unknown hook type" per forum.cursor.com/t/149566). Routing instructions now delivered via server.ts startup (same as Codex CLI) - hooks.json: remove undocumented type/loop_limit/failClosed fields, remove sessionStart entry - hooks.ts: remove sessionStart from REQUIRED_HOOKS - README + docs/platform-support.md: update Cursor sections to reflect verified capabilities Co-Authored-By: Claude Opus 4.6 <[email protected]>
1 parent e29a51e commit eae8dfc

8 files changed

Lines changed: 33 additions & 57 deletions

File tree

README.md

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -199,34 +199,26 @@ npm install -g context-mode
199199
"hooks": {
200200
"preToolUse": [
201201
{
202-
"type": "command",
203202
"command": "context-mode hook cursor pretooluse",
204203
"matcher": "Shell|Read|Grep|WebFetch|Task|MCP:ctx_execute|MCP:ctx_execute_file|MCP:ctx_batch_execute"
205204
}
206205
],
207206
"postToolUse": [
208207
{
209-
"type": "command",
210208
"command": "context-mode hook cursor posttooluse"
211209
}
212-
],
213-
"sessionStart": [
214-
{
215-
"type": "command",
216-
"command": "context-mode hook cursor sessionstart"
217-
}
218210
]
219211
}
220212
}
221213
```
222214

223215
Note: the `preToolUse` hook matcher is optional. If you don't provide it, the hook will fire on all tools.
224216

225-
**Step 4 — Restart Cursor or open a new agent session.**
217+
**Step 4 — Restart Cursor or open a new agent session.** On first MCP server startup, routing instructions are auto-written to your project (same mechanism as Codex CLI).
226218

227-
> **Native Cursor scope:** v1 ships `preToolUse`, `postToolUse`, and `sessionStart`. `preCompact` is intentionally not enabled until real Cursor fixtures confirm compatible compaction semantics.
219+
> **Native Cursor scope:** `preToolUse` and `postToolUse` are supported. `sessionStart` is documented by Cursor but currently rejected by their validator ([forum report](https://forum.cursor.com/t/unknown-hook-type-sessionstart/149566)), so routing instructions are delivered via MCP server startup instead.
228220
>
229-
> **Config precedence:** project `.cursor/hooks.json` overrides `~/.cursor/hooks.json`. Cursor may also load enterprise config at `/Library/Application Support/Cursor/hooks.json`; context-mode treats that layer as read-only informational.
221+
> **Config precedence:** project `.cursor/hooks.json` overrides `~/.cursor/hooks.json`.
230222
231223
Full native hook config: [`configs/cursor/hooks.json`](configs/cursor/hooks.json)
232224
Example MCP registration: [`configs/cursor/mcp.json`](configs/cursor/mcp.json)
@@ -364,10 +356,10 @@ Session continuity requires 4 hooks working together:
364356
| **PostToolUse** | Captures events after each tool call | Yes | Yes | Yes | Yes | Plugin | -- |
365357
| **UserPromptSubmit** | Captures user decisions and corrections | Yes | -- | -- | -- | -- | -- |
366358
| **PreCompact** | Builds snapshot before compaction | Yes | Yes | Yes | -- | Plugin | -- |
367-
| **SessionStart** | Restores state after compaction or resume | Yes | Yes | Yes | Yes | -- | -- |
368-
| | **Session completeness** | **Full** | **High** | **High** | **Medium** | **High** | **--** |
359+
| **SessionStart** | Restores state after compaction or resume | Yes | Yes | Yes | -- | -- | -- |
360+
| | **Session completeness** | **Full** | **High** | **High** | **Partial** | **High** | **--** |
369361

370-
> **Note:** Full session continuity (capture + snapshot + restore) works on **Claude Code**, **Gemini CLI**, and **VS Code Copilot**, and **OpenCode**. **Cursor** v1 restores from persisted session events via `sessionStart`, but intentionally does not advertise `preCompact` until real native fixtures confirm compaction compatibility. **OpenCode** uses the `experimental.session.compacting` plugin hook for compaction recovery, but SessionStart is not yet available ([#14808](https://github.com/sst/opencode/issues/14808)), so startup/resume is not supported. Codex CLI has no hook support, so session tracking is not available.
362+
> **Note:** Full session continuity (capture + snapshot + restore) works on **Claude Code**, **Gemini CLI**, **VS Code Copilot**, and **OpenCode**. **Cursor** captures tool events via `preToolUse`/`postToolUse`, but `sessionStart` is currently rejected by Cursor's validator ([forum report](https://forum.cursor.com/t/unknown-hook-type-sessionstart/149566)), so session restore after compaction is not available yet. **OpenCode** uses the `experimental.session.compacting` plugin hook for compaction recovery, but SessionStart is not yet available ([#14808](https://github.com/sst/opencode/issues/14808)), so startup/resume is not supported. Codex CLI has no hook support, so session tracking is not available.
371363
372364
<details>
373365
<summary><strong>What gets captured</strong></summary>
@@ -442,7 +434,7 @@ Detailed event data is also indexed into FTS5 for on-demand retrieval via `searc
442434

443435
**VS Code Copilot** — High coverage. Same as Gemini CLI — PostToolUse, PreCompact, and SessionStart all fire. User decisions aren't captured but all tool-level events are.
444436

445-
**Cursor**Medium coverage. Native `preToolUse`, `postToolUse`, and `sessionStart` hooks are supported. Session restore works from the persisted event database, but `preCompact` is intentionally disabled in v1 until real Cursor fixtures prove compatible semantics.
437+
**Cursor**Partial coverage. Native `preToolUse` and `postToolUse` hooks capture tool events. `sessionStart` is documented by Cursor but currently rejected by their validator, so session restore is not available. Routing instructions are delivered via MCP server startup instead.
446438

447439
**OpenCode** — Partial. The TypeScript plugin captures PostToolUse events via `tool.execute.after`, but SessionStart is not yet available ([#14808](https://github.com/sst/opencode/issues/14808)). Events are stored but not automatically restored after compaction. The `AGENTS.md` routing instructions file compensates by re-teaching tool preferences at each session start.
448440

@@ -457,7 +449,7 @@ Detailed event data is also indexed into FTS5 for on-demand retrieval via `searc
457449
| MCP Server | Yes | Yes | Yes | Yes | Yes | Yes |
458450
| PreToolUse Hook | Yes | Yes | Yes | Yes | Plugin | -- |
459451
| PostToolUse Hook | Yes | Yes | Yes | Yes | Plugin | -- |
460-
| SessionStart Hook | Yes | Yes | Yes | Yes | -- | -- |
452+
| SessionStart Hook | Yes | Yes | Yes | -- | -- | -- |
461453
| PreCompact Hook | Yes | Yes | Yes | -- | Plugin | -- |
462454
| Can Modify Args | Yes | Yes | Yes | Yes | Plugin | -- |
463455
| Can Block Tools | Yes | Yes | Yes | Yes | Plugin | -- |

configs/cursor/hooks.json

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,13 @@
33
"hooks": {
44
"preToolUse": [
55
{
6-
"type": "command",
76
"command": "context-mode hook cursor pretooluse",
8-
"matcher": "Shell|Read|Grep|WebFetch|mcp_web_fetch|mcp_fetch_tool|Task|MCP:ctx_execute|MCP:ctx_execute_file|MCP:ctx_batch_execute",
9-
"loop_limit": null,
10-
"failClosed": false
7+
"matcher": "Shell|Read|Grep|WebFetch|mcp_web_fetch|mcp_fetch_tool|Task|MCP:ctx_execute|MCP:ctx_execute_file|MCP:ctx_batch_execute"
118
}
129
],
1310
"postToolUse": [
1411
{
15-
"type": "command",
16-
"command": "context-mode hook cursor posttooluse",
17-
"loop_limit": null,
18-
"failClosed": false
19-
}
20-
],
21-
"sessionStart": [
22-
{
23-
"type": "command",
24-
"command": "context-mode hook cursor sessionstart",
25-
"loop_limit": null,
26-
"failClosed": false
12+
"command": "context-mode hook cursor posttooluse"
2713
}
2814
]
2915
}

docs/platform-support.md

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,14 @@ This puts the `context-mode` binary in PATH, which is required for:
3838
| **PreToolUse equivalent** | `PreToolUse` | `BeforeTool` | `PreToolUse` | `preToolUse` | `tool.execute.before` | -- |
3939
| **PostToolUse equivalent** | `PostToolUse` | `AfterTool` | `PostToolUse` | `postToolUse` | `tool.execute.after` | -- |
4040
| **PreCompact equivalent** | `PreCompact` | `PreCompress` | `PreCompact` | -- | `experimental.session.compacting` | -- |
41-
| **SessionStart** | `SessionStart` | `SessionStart` | `SessionStart` | `sessionStart` | -- | -- |
41+
| **SessionStart** | `SessionStart` | `SessionStart` | `SessionStart` | -- (buggy in Cursor) | -- | -- |
4242
| **Can modify args** | Yes | Yes | Yes | Yes | Yes | -- |
4343
| **Can modify output** | Yes | Yes | Yes | No | Yes (caveat) | -- |
4444
| **Can inject session context** | Yes | Yes | Yes | Yes | -- | -- |
4545
| **Can block tools** | Yes | Yes | Yes | Yes | Yes (throw) | -- |
4646
| **Config location** | `~/.claude/settings.json` | `~/.gemini/settings.json` | `.github/hooks/*.json` | `.cursor/hooks.json` or `~/.cursor/hooks.json` | `opencode.json` | `~/.codex/config.toml` |
4747
| **Session ID field** | `session_id` | `session_id` | `sessionId` (camelCase) | `conversation_id` | `sessionID` (camelCase) | N/A |
48-
| **Project dir env** | `CLAUDE_PROJECT_DIR` | `GEMINI_PROJECT_DIR` | `CLAUDE_PROJECT_DIR` | `CURSOR_CWD` | `ctx.directory` (plugin init) | N/A |
48+
| **Project dir env** | `CLAUDE_PROJECT_DIR` | `GEMINI_PROJECT_DIR` | `CLAUDE_PROJECT_DIR` | stdin `workspace_roots` | `ctx.directory` (plugin init) | N/A |
4949
| **MCP tool naming** | `mcp__server__tool` | `mcp__server__tool` | `f1e_` prefix | `MCP:<tool>` in hook payloads | `mcp__server__tool` | `mcp__server__tool` |
5050
| **Hook command format** | `context-mode hook claude-code <event>` | `context-mode hook gemini-cli <event>` | `context-mode hook vscode-copilot <event>` | `context-mode hook cursor <event>` | TS plugin (no command) | N/A |
5151
| **Hook registration** | settings.json hooks object | settings.json hooks object | `.github/hooks/*.json` | `hooks.json` native hook arrays | opencode.json plugin array | N/A |
@@ -277,26 +277,29 @@ Cursor uses native lower-camel hook names and flat hook entries in `.cursor/hook
277277
**Hook Names:**
278278
- `preToolUse` -- fires before a tool is executed
279279
- `postToolUse` -- fires after a tool completes
280-
- `sessionStart` -- fires on startup, resume, and compact
280+
- `sessionStart` -- documented but currently rejected by Cursor's validator ([forum report](https://forum.cursor.com/t/unknown-hook-type-sessionstart/149566))
281281

282282
**Blocking:** `{ "permission": "deny", "user_message": "..." }`
283283

284-
**Arg Modification:** `{ "updated_input": { ... } }`
284+
**Arg Modification:** not natively supported (Cursor does not have `updated_input`)
285285

286286
**Output Modification:** not supported in v1
287287

288288
**Session Context Injection:** `{ "additional_context": "..." }`
289289

290290
**Session ID Extraction Priority:**
291-
1. `conversation_id`
292-
2. `CURSOR_SESSION_ID` environment variable
293-
3. `CURSOR_TRACE_ID` environment variable
294-
4. Parent process ID fallback
291+
1. `conversation_id` (stdin JSON)
292+
2. `CURSOR_TRACE_ID` environment variable
293+
3. Parent process ID fallback
294+
295+
**Platform Detection Env Vars:**
296+
- `CURSOR_TRACE_ID` (MCP server context)
297+
- `CURSOR_CLI` (integrated terminal context)
298+
- `~/.cursor/` directory fallback (medium confidence)
295299

296300
**Configuration:**
297301
- Project: `.cursor/hooks.json`
298302
- User: `~/.cursor/hooks.json`
299-
- Enterprise: `/Library/Application Support/Cursor/hooks.json` (read-only informational layer)
300303
- MCP config: `.cursor/mcp.json` or `~/.cursor/mcp.json`
301304

302305
**Hook Commands:**

src/adapters/cursor/hooks.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ export const PRE_TOOL_USE_MATCHER_PATTERN = PRE_TOOL_USE_MATCHERS.join("|");
4141
/** Required hooks for native Cursor support. */
4242
export const REQUIRED_HOOKS: HookType[] = [
4343
HOOK_TYPES.PRE_TOOL_USE,
44-
HOOK_TYPES.SESSION_START,
4544
];
4645

4746
/** Optional hooks that improve behavior but aren't strictly required. */

src/adapters/cursor/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export class CursorAdapter implements HookAdapter {
7575
preToolUse: true,
7676
postToolUse: true,
7777
preCompact: false,
78-
sessionStart: true,
78+
sessionStart: false,
7979
canModifyArgs: true,
8080
canModifyOutput: false,
8181
canInjectSessionContext: true,

src/adapters/detect.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* - Gemini CLI: GEMINI_PROJECT_DIR (hooks), GEMINI_CLI (MCP) | ~/.gemini/
1212
* - OpenCode: OPENCODE, OPENCODE_PID | ~/.config/opencode/
1313
* - Codex CLI: CODEX_CI, CODEX_THREAD_ID | ~/.codex/
14-
* - Cursor: CURSOR_CWD, CURSOR_SESSION_ID, CURSOR_TRACE_ID | ~/.cursor/
14+
* - Cursor: CURSOR_TRACE_ID (MCP), CURSOR_CLI (terminal) | ~/.cursor/
1515
* - VS Code Copilot: VSCODE_PID, VSCODE_CWD | ~/.vscode/
1616
*/
1717

@@ -59,15 +59,11 @@ export function detectPlatform(): DetectionSignal {
5959
};
6060
}
6161

62-
if (
63-
process.env.CURSOR_CWD
64-
|| process.env.CURSOR_SESSION_ID
65-
|| process.env.CURSOR_TRACE_ID
66-
) {
62+
if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CLI) {
6763
return {
6864
platform: "cursor",
6965
confidence: "high",
70-
reason: "CURSOR_CWD, CURSOR_SESSION_ID, or CURSOR_TRACE_ID env var set",
66+
reason: "CURSOR_TRACE_ID or CURSOR_CLI env var set",
7167
};
7268
}
7369

tests/adapters/cursor.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe("CursorAdapter", () => {
2020
it("enables native Cursor v1 hooks without preCompact", () => {
2121
expect(adapter.capabilities.preToolUse).toBe(true);
2222
expect(adapter.capabilities.postToolUse).toBe(true);
23-
expect(adapter.capabilities.sessionStart).toBe(true);
23+
expect(adapter.capabilities.sessionStart).toBe(false);
2424
expect(adapter.capabilities.preCompact).toBe(false);
2525
expect(adapter.capabilities.canModifyArgs).toBe(true);
2626
expect(adapter.capabilities.canModifyOutput).toBe(false);
@@ -233,7 +233,7 @@ describe("CursorAdapter", () => {
233233
expect(results[0]?.check).toBe("Native hook config");
234234
expect(results[0]?.status).toBe("pass");
235235
expect(results.find((result) => result.check === "preToolUse")?.status).toBe("pass");
236-
expect(results.find((result) => result.check === "sessionStart")?.status).toBe("pass");
236+
// sessionStart is not validated — Cursor rejects it currently
237237
expect(results.find((result) => result.check === "postToolUse")?.status).toBe("warn");
238238
expect(results.find((result) => result.check === "Claude compatibility")?.status).toBe("warn");
239239
});

tests/adapters/detect.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -103,22 +103,22 @@ describe("detectPlatform", () => {
103103

104104
// ── Cursor ─────────────────────────────────────────────
105105

106-
it("returns cursor when CURSOR_CWD is set", () => {
107-
process.env.CURSOR_CWD = "/some/dir";
106+
it("returns cursor when CURSOR_TRACE_ID is set", () => {
107+
process.env.CURSOR_TRACE_ID = "trace-abc-123";
108108
const signal = detectPlatform();
109109
expect(signal.platform).toBe("cursor");
110110
expect(signal.confidence).toBe("high");
111111
});
112112

113-
it("returns cursor when CURSOR_SESSION_ID is set", () => {
114-
process.env.CURSOR_SESSION_ID = "cursor-sess-123";
113+
it("returns cursor when CURSOR_CLI is set", () => {
114+
process.env.CURSOR_CLI = "1";
115115
const signal = detectPlatform();
116116
expect(signal.platform).toBe("cursor");
117117
expect(signal.confidence).toBe("high");
118118
});
119119

120120
it("prefers cursor over vscode-copilot when both Cursor and VS Code env vars are set", () => {
121-
process.env.CURSOR_CWD = "/cursor/project";
121+
process.env.CURSOR_TRACE_ID = "trace-abc-123";
122122
process.env.VSCODE_PID = "12345";
123123
const signal = detectPlatform();
124124
expect(signal.platform).toBe("cursor");

0 commit comments

Comments
 (0)