diff --git a/internal/setup/agents.go b/internal/setup/agents.go index cd09506b..51e619a7 100644 --- a/internal/setup/agents.go +++ b/internal/setup/agents.go @@ -118,16 +118,18 @@ func agentAdapters() []agentAdapter { }, { slug: "cursor", - description: "Cursor — MCP registration in ~/.cursor/mcp.json plus an always-applied .mdc rule", + description: "Cursor — MCP registration in ~/.cursor/mcp.json plus an informational Memory Protocol file to paste as a User Rule", mcpPath: cursorMCPPath, mcpFormat: mcpServersObject, instructions: []instrSurface{ - {path: cursorRulesPath, style: wholeFile, body: cursorRulesBody()}, + {path: cursorMemoryProtocolPath, style: wholeFile, body: memoryProtocolMarkdown}, }, postInstall: []string{ "Restart Cursor so MCP config is reloaded", "Verify ~/.cursor/mcp.json includes mcpServers.engram", - "Verify ~/.cursor/rules/engram.mdc exists (always-applied rule)", + "NOTE: Cursor does NOT read global rule files from the filesystem — .mdc files outside a project are silently ignored", + "Open ~/.cursor/engram-memory-protocol.md and copy its contents", + "In Cursor, open Settings → Rules → User Rules and paste the copied contents", }, }, { @@ -161,13 +163,6 @@ func agentAdapters() []agentAdapter { } } -// cursorRulesBody wraps the Memory Protocol in the YAML frontmatter Cursor needs -// for an always-applied rule (alwaysApply:true ignores globs and attaches the rule -// regardless of context). -func cursorRulesBody() string { - return "---\ndescription: Engram persistent memory protocol\nalwaysApply: true\n---\n\n" + memoryProtocolMarkdown -} - // vscodeInstructionsBody wraps the Memory Protocol in the frontmatter VS Code // Copilot uses for a user-level instructions file that applies to every file. func vscodeInstructionsBody() string { @@ -237,9 +232,14 @@ func cursorMCPPath() string { return filepath.Join(home, ".cursor", "mcp.json") } -func cursorRulesPath() string { +// cursorMemoryProtocolPath returns the path to the informational Memory Protocol +// file for Cursor. Cursor does not read global rule files from the filesystem; +// .mdc files with alwaysApply outside a project are silently ignored. This file +// is intended to be opened by the user and its contents pasted into +// Settings → Rules → User Rules inside Cursor. +func cursorMemoryProtocolPath() string { home, _ := userHome() - return filepath.Join(home, ".cursor", "rules", "engram.mdc") + return filepath.Join(home, ".cursor", "engram-memory-protocol.md") } // ─── VS Code (Copilot) paths ───────────────────────────────────────────────── diff --git a/internal/setup/registry_test.go b/internal/setup/registry_test.go index 8cbe16a2..75b0009d 100644 --- a/internal/setup/registry_test.go +++ b/internal/setup/registry_test.go @@ -28,7 +28,7 @@ func declarativeAgents() []declarativeAgent { {"windsurf", windsurfMCPPath, "mcpServers", mcpServersObject, windsurfRulesPath, markerBlock}, {"qwen", qwenSettingsPath, "mcpServers", mcpServersObject, qwenContextPath, markerBlock}, {"kiro", kiroMCPPath, "mcpServers", mcpServersObject, kiroSteeringPath, markerBlock}, - {"cursor", cursorMCPPath, "mcpServers", mcpServersObject, cursorRulesPath, wholeFile}, + {"cursor", cursorMCPPath, "mcpServers", mcpServersObject, cursorMemoryProtocolPath, wholeFile}, {"vscode-copilot", vscodeMCPPath, "servers", serversObject, vscodePromptPath, wholeFile}, {"kilocode", kilocodeConfigPath, "mcp", opencodeObject, kilocodeAgentsPath, markerBlock}, } @@ -180,16 +180,38 @@ func TestInstallDeclarativeAgentsRegisterMCPAndInstructions(t *testing.T) { } } -func TestCursorAndVSCodeInstructionsCarryFrontmatter(t *testing.T) { - stubRegistryEnv(t) +func TestCursorMemoryProtocolHasNoFrontmatter(t *testing.T) { + home := stubRegistryEnv(t) if _, err := Install("cursor"); err != nil { t.Fatalf("Install(cursor): %v", err) } - cursorRaw, _ := os.ReadFile(cursorRulesPath()) - if !strings.Contains(string(cursorRaw), "alwaysApply: true") { - t.Errorf("cursor .mdc missing alwaysApply frontmatter") + + // The old .mdc path must NOT exist — we no longer write a global rule file. + oldMDCPath := filepath.Join(home, ".cursor", "rules", "engram.mdc") + if _, err := os.Stat(oldMDCPath); err == nil { + t.Errorf("cursor: ~/.cursor/rules/engram.mdc should not be written (Cursor ignores global rule files)") + } + + // The new informational file must exist, contain the protocol, and have no YAML frontmatter. + protocolRaw, err := os.ReadFile(cursorMemoryProtocolPath()) + if err != nil { + t.Fatalf("cursor: memory protocol file not written at %s: %v", cursorMemoryProtocolPath(), err) + } + content := string(protocolRaw) + if !strings.Contains(content, "Engram Persistent Memory") { + t.Errorf("cursor: memory protocol file missing expected content") } + if strings.Contains(content, "alwaysApply") { + t.Errorf("cursor: memory protocol file must not contain alwaysApply frontmatter") + } + if strings.HasPrefix(content, "---") { + t.Errorf("cursor: memory protocol file must not start with YAML frontmatter") + } +} + +func TestVSCodeInstructionsCarryFrontmatter(t *testing.T) { + stubRegistryEnv(t) if _, err := Install("vscode-copilot"); err != nil { t.Fatalf("Install(vscode-copilot): %v", err)