Skip to content

Commit 9ee6b7f

Browse files
fix(setup): stop writing unread Cursor global rule, write informational copy (#522)
Cursor does not read global rule files from the filesystem — .mdc files with alwaysApply outside a project are silently ignored (global User Rules are UI-only, plain text, no path). So engram setup cursor was writing ~/.cursor/rules/engram.mdc that Cursor never read. Instead write the Memory Protocol to ~/.cursor/engram-memory-protocol.md (plain, no frontmatter) and instruct the user via postInstall to paste it as a User Rule in Settings → Rules. MCP registration is unchanged. Removes dead cursorRulesBody/cursorRulesPath; updates tests. Closes #521.
1 parent 980ebd8 commit 9ee6b7f

2 files changed

Lines changed: 40 additions & 18 deletions

File tree

internal/setup/agents.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -118,16 +118,18 @@ func agentAdapters() []agentAdapter {
118118
},
119119
{
120120
slug: "cursor",
121-
description: "Cursor — MCP registration in ~/.cursor/mcp.json plus an always-applied .mdc rule",
121+
description: "Cursor — MCP registration in ~/.cursor/mcp.json plus an informational Memory Protocol file to paste as a User Rule",
122122
mcpPath: cursorMCPPath,
123123
mcpFormat: mcpServersObject,
124124
instructions: []instrSurface{
125-
{path: cursorRulesPath, style: wholeFile, body: cursorRulesBody()},
125+
{path: cursorMemoryProtocolPath, style: wholeFile, body: memoryProtocolMarkdown},
126126
},
127127
postInstall: []string{
128128
"Restart Cursor so MCP config is reloaded",
129129
"Verify ~/.cursor/mcp.json includes mcpServers.engram",
130-
"Verify ~/.cursor/rules/engram.mdc exists (always-applied rule)",
130+
"NOTE: Cursor does NOT read global rule files from the filesystem — .mdc files outside a project are silently ignored",
131+
"Open ~/.cursor/engram-memory-protocol.md and copy its contents",
132+
"In Cursor, open Settings → Rules → User Rules and paste the copied contents",
131133
},
132134
},
133135
{
@@ -161,13 +163,6 @@ func agentAdapters() []agentAdapter {
161163
}
162164
}
163165

164-
// cursorRulesBody wraps the Memory Protocol in the YAML frontmatter Cursor needs
165-
// for an always-applied rule (alwaysApply:true ignores globs and attaches the rule
166-
// regardless of context).
167-
func cursorRulesBody() string {
168-
return "---\ndescription: Engram persistent memory protocol\nalwaysApply: true\n---\n\n" + memoryProtocolMarkdown
169-
}
170-
171166
// vscodeInstructionsBody wraps the Memory Protocol in the frontmatter VS Code
172167
// Copilot uses for a user-level instructions file that applies to every file.
173168
func vscodeInstructionsBody() string {
@@ -237,9 +232,14 @@ func cursorMCPPath() string {
237232
return filepath.Join(home, ".cursor", "mcp.json")
238233
}
239234

240-
func cursorRulesPath() string {
235+
// cursorMemoryProtocolPath returns the path to the informational Memory Protocol
236+
// file for Cursor. Cursor does not read global rule files from the filesystem;
237+
// .mdc files with alwaysApply outside a project are silently ignored. This file
238+
// is intended to be opened by the user and its contents pasted into
239+
// Settings → Rules → User Rules inside Cursor.
240+
func cursorMemoryProtocolPath() string {
241241
home, _ := userHome()
242-
return filepath.Join(home, ".cursor", "rules", "engram.mdc")
242+
return filepath.Join(home, ".cursor", "engram-memory-protocol.md")
243243
}
244244

245245
// ─── VS Code (Copilot) paths ─────────────────────────────────────────────────

internal/setup/registry_test.go

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ func declarativeAgents() []declarativeAgent {
2828
{"windsurf", windsurfMCPPath, "mcpServers", mcpServersObject, windsurfRulesPath, markerBlock},
2929
{"qwen", qwenSettingsPath, "mcpServers", mcpServersObject, qwenContextPath, markerBlock},
3030
{"kiro", kiroMCPPath, "mcpServers", mcpServersObject, kiroSteeringPath, markerBlock},
31-
{"cursor", cursorMCPPath, "mcpServers", mcpServersObject, cursorRulesPath, wholeFile},
31+
{"cursor", cursorMCPPath, "mcpServers", mcpServersObject, cursorMemoryProtocolPath, wholeFile},
3232
{"vscode-copilot", vscodeMCPPath, "servers", serversObject, vscodePromptPath, wholeFile},
3333
{"kilocode", kilocodeConfigPath, "mcp", opencodeObject, kilocodeAgentsPath, markerBlock},
3434
}
@@ -180,16 +180,38 @@ func TestInstallDeclarativeAgentsRegisterMCPAndInstructions(t *testing.T) {
180180
}
181181
}
182182

183-
func TestCursorAndVSCodeInstructionsCarryFrontmatter(t *testing.T) {
184-
stubRegistryEnv(t)
183+
func TestCursorMemoryProtocolHasNoFrontmatter(t *testing.T) {
184+
home := stubRegistryEnv(t)
185185

186186
if _, err := Install("cursor"); err != nil {
187187
t.Fatalf("Install(cursor): %v", err)
188188
}
189-
cursorRaw, _ := os.ReadFile(cursorRulesPath())
190-
if !strings.Contains(string(cursorRaw), "alwaysApply: true") {
191-
t.Errorf("cursor .mdc missing alwaysApply frontmatter")
189+
190+
// The old .mdc path must NOT exist — we no longer write a global rule file.
191+
oldMDCPath := filepath.Join(home, ".cursor", "rules", "engram.mdc")
192+
if _, err := os.Stat(oldMDCPath); err == nil {
193+
t.Errorf("cursor: ~/.cursor/rules/engram.mdc should not be written (Cursor ignores global rule files)")
194+
}
195+
196+
// The new informational file must exist, contain the protocol, and have no YAML frontmatter.
197+
protocolRaw, err := os.ReadFile(cursorMemoryProtocolPath())
198+
if err != nil {
199+
t.Fatalf("cursor: memory protocol file not written at %s: %v", cursorMemoryProtocolPath(), err)
200+
}
201+
content := string(protocolRaw)
202+
if !strings.Contains(content, "Engram Persistent Memory") {
203+
t.Errorf("cursor: memory protocol file missing expected content")
192204
}
205+
if strings.Contains(content, "alwaysApply") {
206+
t.Errorf("cursor: memory protocol file must not contain alwaysApply frontmatter")
207+
}
208+
if strings.HasPrefix(content, "---") {
209+
t.Errorf("cursor: memory protocol file must not start with YAML frontmatter")
210+
}
211+
}
212+
213+
func TestVSCodeInstructionsCarryFrontmatter(t *testing.T) {
214+
stubRegistryEnv(t)
193215

194216
if _, err := Install("vscode-copilot"); err != nil {
195217
t.Fatalf("Install(vscode-copilot): %v", err)

0 commit comments

Comments
 (0)