From c0b5b37a41a6c66aad4d9a0cec2586ef04908362 Mon Sep 17 00:00:00 2001 From: rodriabregu Date: Wed, 9 Sep 2026 23:07:17 -0300 Subject: [PATCH] feat(setup): add cline agent adapter Register Cline as a declarative agent so `engram setup cline` wires the MCP server and the Memory Protocol without a manual config edit. The adapter writes mcpServers.engram to ~/.cline/data/settings/cline_mcp_settings.json and owns ~/.cline/rules/engram.md as a whole file, since Cline combines every .md under ~/.cline/rules/ into a single ruleset. Closes #554 --- README.md | 1 + cmd/engram/main.go | 2 +- cmd/engram/main_test.go | 7 ++++++- docs/AGENT-SETUP.md | 13 +++++++++++++ internal/setup/agents.go | 33 +++++++++++++++++++++++++++++++++ internal/setup/registry_test.go | 3 ++- 6 files changed, 56 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 6aeec01b3..62ce1fcaa 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ Run the setup command for the agent you use, then restart that agent. `engram se | Cursor | `engram setup cursor` | | VS Code (Copilot) | `engram setup vscode-copilot` | | Kilo Code | `engram setup kilocode` | +| Cline | `engram setup cline` | | Another MCP-compatible agent | [Manual MCP setup](docs/AGENT-SETUP.md#any-other-mcp-agent) | See [Agent Setup](docs/AGENT-SETUP.md) for per-agent configuration, plugin behavior, manual MCP setup, compaction resilience, and troubleshooting. Pi users can also find the package at [`gentle-engram`](plugin/pi/README.md). diff --git a/cmd/engram/main.go b/cmd/engram/main.go index 0bb6afae8..4fa1e705d 100644 --- a/cmd/engram/main.go +++ b/cmd/engram/main.go @@ -3212,7 +3212,7 @@ Commands: --paths-only Limit pruning to project names containing / or \ setup [agent] Install/setup agent integration (opencode, pi, claude-code, gemini-cli, codex, antigravity-cli, windsurf, qwen, kiro, - cursor, vscode-copilot, kilocode) + cursor, vscode-copilot, kilocode, cline) sync Export new memories as compressed chunk to .engram/ --import Import new chunks from .engram/ into local DB --status Show sync status diff --git a/cmd/engram/main_test.go b/cmd/engram/main_test.go index c92412165..212547ded 100644 --- a/cmd/engram/main_test.go +++ b/cmd/engram/main_test.go @@ -309,7 +309,7 @@ func TestPrintUsage(t *testing.T) { if !strings.Contains(stdout, "search ") || !strings.Contains(stdout, "setup [agent]") { t.Fatalf("usage missing expected commands: %q", stdout) } - for _, agent := range []string{"opencode", "pi", "claude-code", "gemini-cli", "codex", "antigravity-cli", "windsurf", "qwen", "kiro", "cursor", "vscode-copilot", "kilocode"} { + for _, agent := range []string{"opencode", "pi", "claude-code", "gemini-cli", "codex", "antigravity-cli", "windsurf", "qwen", "kiro", "cursor", "vscode-copilot", "kilocode", "cline"} { if !strings.Contains(stdout, agent) { t.Fatalf("usage missing setup agent %q: %q", agent, stdout) } @@ -427,6 +427,11 @@ func TestPrintPostInstall(t *testing.T) { result: &setup.Result{Agent: "kilocode"}, expects: []string{"Restart Kilo Code", "~/.config/kilo/opencode.json"}, }, + { + name: "cline", + result: &setup.Result{Agent: "cline"}, + expects: []string{"Restart Cline", "~/.cline/data/settings/cline_mcp_settings.json", "~/.cline/rules/engram.md"}, + }, { name: "unknown", result: &setup.Result{Agent: "unknown"}, diff --git a/docs/AGENT-SETUP.md b/docs/AGENT-SETUP.md index a065efd6a..d2a555fcd 100644 --- a/docs/AGENT-SETUP.md +++ b/docs/AGENT-SETUP.md @@ -28,6 +28,7 @@ Engram works with **any MCP-compatible agent**. Pick your agent below. | Cursor | `engram setup cursor` | [Details](#cursor) | | VS Code Copilot | `engram setup vscode-copilot` | [Details](#vs-code-copilot--claude-code-extension) | | Kilo Code | `engram setup kilocode` | [Details](#kilo-code) | +| Cline | `engram setup cline` | [Details](#cline) | | Any MCP agent | `engram mcp` (stdio) | [Details](#any-other-mcp-agent) | > **Native setup for all agents above.** `engram setup ` writes the right @@ -719,6 +720,18 @@ Registers the engram server under the OpenCode-style `mcp` object in `~/.config/ --- +## Cline + +**Automated:** + +```bash +engram setup cline +``` + +Registers `mcpServers.engram` in `~/.cline/data/settings/cline_mcp_settings.json` and writes the Memory Protocol to `~/.cline/rules/engram.md`. Cline combines every `.md` file in `~/.cline/rules/` into one ruleset, so the protocol gets its own engram-owned file there instead of sharing one with your own rules. + +--- + ## Any other MCP agent The pattern is always the same — point your agent's MCP config to `engram mcp` via stdio transport. diff --git a/internal/setup/agents.go b/internal/setup/agents.go index a035696d0..f53978028 100644 --- a/internal/setup/agents.go +++ b/internal/setup/agents.go @@ -160,6 +160,20 @@ func agentAdapters() []agentAdapter { "Verify ~/.config/kilo/AGENTS.md has the Memory Protocol block", }, }, + { + slug: "cline", + description: "Cline — MCP registration in ~/.cline/data/settings/cline_mcp_settings.json plus a dedicated rules file Memory Protocol", + mcpPath: clineMCPPath, + mcpFormat: mcpServersObject, + instructions: []instrSurface{ + {path: clineRulesPath, style: wholeFile, body: memoryProtocolMarkdown}, + }, + postInstall: []string{ + "Restart Cline so MCP config is reloaded", + "Verify ~/.cline/data/settings/cline_mcp_settings.json includes mcpServers.engram", + "Verify ~/.cline/rules/engram.md has the Memory Protocol", + }, + }, } } @@ -291,3 +305,22 @@ func kilocodeConfigPath() string { func kilocodeAgentsPath() string { return filepath.Join(kilocodeConfigDir(), "AGENTS.md") } + +// ─── Cline paths ───────────────────────────────────────────────────────────── +// +// Cline stores its MCP registration under ~/.cline/data/settings/ and combines +// every .md file in ~/.cline/rules/ into one ruleset, so the Memory Protocol +// gets its own engram-owned file there instead of a marker block in a shared one. + +func clineDir() string { + home, _ := userHome() + return filepath.Join(home, ".cline") +} + +func clineMCPPath() string { + return filepath.Join(clineDir(), "data", "settings", "cline_mcp_settings.json") +} + +func clineRulesPath() string { + return filepath.Join(clineDir(), "rules", "engram.md") +} diff --git a/internal/setup/registry_test.go b/internal/setup/registry_test.go index 8138cfa4a..cb1bdb834 100644 --- a/internal/setup/registry_test.go +++ b/internal/setup/registry_test.go @@ -31,6 +31,7 @@ func declarativeAgents() []declarativeAgent { {"cursor", cursorMCPPath, "mcpServers", mcpServersObject, cursorMemoryProtocolPath, wholeFile}, {"vscode-copilot", vscodeMCPPath, "servers", serversObject, vscodePromptPath, wholeFile}, {"kilocode", kilocodeConfigPath, "mcp", opencodeObject, kilocodeAgentsPath, markerBlock}, + {"cline", clineMCPPath, "mcpServers", mcpServersObject, clineRulesPath, wholeFile}, } } @@ -58,7 +59,7 @@ func TestSupportedAgentsIncludesAllRegistryAgents(t *testing.T) { want := []string{ "opencode", "pi", "claude-code", "gemini-cli", "codex", "antigravity-cli", "windsurf", "qwen", "kiro", "cursor", - "vscode-copilot", "kilocode", + "vscode-copilot", "kilocode", "cline", } for _, slug := range want { if !got[slug] {