From 63b57e9c132282400885418156a795308da8e605 Mon Sep 17 00:00:00 2001 From: JAAvila Date: Sat, 18 Jul 2026 11:10:35 -0500 Subject: [PATCH] feat(setup): add kimi agent adapter for Kimi Code CLI Kimi Code CLI had no one-liner setup: users had to hand-edit mcp.json to register the engram MCP server and paste the Memory Protocol into AGENTS.md themselves. This adds a declarative "kimi" adapter to the setup registry so "engram setup kimi" registers mcpServers.engram in $KIMI_CODE_HOME/mcp.json and writes the Memory Protocol as a marker-delimited block in $KIMI_CODE_HOME/AGENTS.md, both idempotently and preserving existing content. The adapter is declarative (same shape as qwen/windsurf/kiro) rather than a kimi.plugin.json plugin because Kimi Code plugins only install interactively from the /plugins TUI, while mcp.json and AGENTS.md are documented stable surfaces a CLI installer can write safely. - honor KIMI_CODE_HOME when set, ignoring relative values like the existing XDG helpers - cover the registry entry, slug list, env handling, CLI usage and post-install output with tests - document the new agent in README, AGENT-SETUP, INSTALLATION and codebase/integrations Refs #650 --- README.md | 1 + cmd/engram/main.go | 2 +- cmd/engram/main_test.go | 7 ++++++- docs/AGENT-SETUP.md | 13 ++++++++++++ docs/INSTALLATION.md | 1 + docs/codebase/integrations.md | 2 +- internal/setup/agents.go | 37 +++++++++++++++++++++++++++++++++ internal/setup/registry_test.go | 31 ++++++++++++++++++++++++++- 8 files changed, 90 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6d0c04107..dba58ddf6 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ Windows, Linux, and other install methods → [docs/INSTALLATION.md](docs/INSTAL | Cursor | `engram setup cursor` | | VS Code (Copilot) | `engram setup vscode-copilot` | | Kilo Code | `engram setup kilocode` | +| Kimi Code | `engram setup kimi` | | Any other MCP client | See [docs/AGENT-SETUP.md](docs/AGENT-SETUP.md) | Full per-agent config, Memory Protocol, and compaction survival → [docs/AGENT-SETUP.md](docs/AGENT-SETUP.md) diff --git a/cmd/engram/main.go b/cmd/engram/main.go index 730d783f9..844ccda6d 100644 --- a/cmd/engram/main.go +++ b/cmd/engram/main.go @@ -2681,7 +2681,7 @@ Commands: --dry-run Preview what would be merged (no changes) 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, kimi) 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 76fbc7c06..ade3c7ae2 100644 --- a/cmd/engram/main_test.go +++ b/cmd/engram/main_test.go @@ -166,7 +166,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", "kimi"} { if !strings.Contains(stdout, agent) { t.Fatalf("usage missing setup agent %q: %q", agent, stdout) } @@ -266,6 +266,11 @@ func TestPrintPostInstall(t *testing.T) { result: &setup.Result{Agent: "kilocode"}, expects: []string{"Restart Kilo Code", "~/.config/kilo/opencode.json"}, }, + { + name: "kimi", + result: &setup.Result{Agent: "kimi"}, + expects: []string{"Restart Kimi Code", "~/.kimi-code/mcp.json", "~/.kimi-code/AGENTS.md"}, + }, { name: "unknown", result: &setup.Result{Agent: "unknown"}, diff --git a/docs/AGENT-SETUP.md b/docs/AGENT-SETUP.md index c57f0a8cf..3abcbf65a 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) | +| Kimi Code | `engram setup kimi` | [Details](#kimi-code) | | Any MCP agent | `engram mcp` (stdio) | [Details](#any-other-mcp-agent) | > **Native setup for all agents above.** `engram setup ` writes the right @@ -691,6 +692,18 @@ Registers the engram server under the OpenCode-style `mcp` object in `~/.config/ --- +## Kimi Code + +**Automated:** + +```bash +engram setup kimi +``` + +Registers `mcpServers.engram` in `~/.kimi-code/mcp.json` and writes the Memory Protocol as a marker block in `~/.kimi-code/AGENTS.md`. Both files live under the Kimi Code data root, so when `KIMI_CODE_HOME` is set the setup honors it and writes there instead. + +--- + ## 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/docs/INSTALLATION.md b/docs/INSTALLATION.md index 3923197dc..16bfe00ba 100644 --- a/docs/INSTALLATION.md +++ b/docs/INSTALLATION.md @@ -197,4 +197,5 @@ When using `engram setup`, config files are written to platform-appropriate loca | Cursor | `~/.cursor/mcp.json` + `~/.cursor/rules/engram.mdc` | `%USERPROFILE%\.cursor\...` | | VS Code Copilot | `~/.config/Code/User/mcp.json` + `.../prompts/engram.instructions.md` (macOS: `~/Library/Application Support/Code/User/`) | `%APPDATA%\Code\User\...` | | Kilo Code | `~/.config/kilo/opencode.json` + `~/.config/kilo/AGENTS.md` | `%USERPROFILE%\.config\kilo\...` | +| Kimi Code | `~/.kimi-code/mcp.json` + `~/.kimi-code/AGENTS.md` | `%USERPROFILE%\.kimi-code\...` | | Data directory | `~/.engram/` | `%USERPROFILE%\.engram\` | diff --git a/docs/codebase/integrations.md b/docs/codebase/integrations.md index ba3a4c23e..3898c15a6 100644 --- a/docs/codebase/integrations.md +++ b/docs/codebase/integrations.md @@ -39,7 +39,7 @@ is either: - **declarative** — just an MCP path + format (`mcpServers` / `servers` / OpenCode's `mcp` object) and instruction surfaces; the generic `injectMCP` / `writeInstruction` driver in `registry.go` does the writes. Antigravity CLI, Windsurf, Qwen, Kiro, - Cursor, VS Code Copilot, and Kilo Code are all declarative. + Cursor, VS Code Copilot, Kilo Code, and Kimi Code are all declarative. Adding a declarative agent is normally just a new entry in `agentAdapters()` plus its path helpers — no new install code path. Agents not in the registry remain diff --git a/internal/setup/agents.go b/internal/setup/agents.go index 51e619a7f..4aa46b51c 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: "kimi", + description: "Kimi Code CLI — MCP registration in ~/.kimi-code/mcp.json plus AGENTS.md Memory Protocol", + mcpPath: kimiMCPPath, + mcpFormat: mcpServersObject, + instructions: []instrSurface{ + {path: kimiAgentsPath, style: markerBlock, body: memoryProtocolMarkdown}, + }, + postInstall: []string{ + "Restart Kimi Code so MCP config is reloaded", + "Verify ~/.kimi-code/mcp.json includes mcpServers.engram", + "Verify ~/.kimi-code/AGENTS.md has the Memory Protocol block", + }, + }, } } @@ -291,3 +305,26 @@ func kilocodeConfigPath() string { func kilocodeAgentsPath() string { return filepath.Join(kilocodeConfigDir(), "AGENTS.md") } + +// ─── Kimi Code paths ───────────────────────────────────────────────────────── +// +// Kimi Code keeps all user-level data under KIMI_CODE_HOME (default +// ~/.kimi-code on every platform, including Windows). MCP servers are declared +// in mcp.json (top-level "mcpServers") and global agent instructions in +// AGENTS.md; both live directly under the data root. + +func kimiCodeHome() string { + if dir := os.Getenv("KIMI_CODE_HOME"); dir != "" && filepath.IsAbs(dir) { + return dir + } + home, _ := userHome() + return filepath.Join(home, ".kimi-code") +} + +func kimiMCPPath() string { + return filepath.Join(kimiCodeHome(), "mcp.json") +} + +func kimiAgentsPath() string { + return filepath.Join(kimiCodeHome(), "AGENTS.md") +} diff --git a/internal/setup/registry_test.go b/internal/setup/registry_test.go index ad23d7552..247b0979b 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}, + {"kimi", kimiMCPPath, "mcpServers", mcpServersObject, kimiAgentsPath, markerBlock}, } } @@ -44,6 +45,7 @@ func stubRegistryEnv(t *testing.T) string { osExecutable = func() (string, error) { return testEngramBin, nil } t.Setenv("XDG_CONFIG_HOME", "") t.Setenv("APPDATA", "") + t.Setenv("KIMI_CODE_HOME", "") return home } @@ -58,7 +60,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", "kimi", } for _, slug := range want { if !got[slug] { @@ -436,4 +438,31 @@ func TestConfigDirsIgnoreRelativeConfigHome(t *testing.T) { t.Errorf("vscodeUserDir with relative APPDATA = %q, want %q", got, want) } }) + + t.Run("relative KIMI_CODE_HOME ignored", func(t *testing.T) { + t.Setenv("KIMI_CODE_HOME", "relative/kimi") + if got, want := kimiCodeHome(), filepath.Join(home, ".kimi-code"); got != want { + t.Errorf("kimiCodeHome with relative KIMI_CODE_HOME = %q, want %q", got, want) + } + }) +} + +// TestKimiCodeHomeHonorsAbsoluteEnv verifies an absolute KIMI_CODE_HOME +// relocates both the MCP config and the AGENTS.md instruction surface. +func TestKimiCodeHomeHonorsAbsoluteEnv(t *testing.T) { + resetSetupSeams(t) + useTestHome(t) + + custom := filepath.Join(t.TempDir(), "kimi-home") + t.Setenv("KIMI_CODE_HOME", custom) + + if got := kimiCodeHome(); got != custom { + t.Errorf("kimiCodeHome = %q, want %q", got, custom) + } + if got, want := kimiMCPPath(), filepath.Join(custom, "mcp.json"); got != want { + t.Errorf("kimiMCPPath = %q, want %q", got, want) + } + if got, want := kimiAgentsPath(), filepath.Join(custom, "AGENTS.md"); got != want { + t.Errorf("kimiAgentsPath = %q, want %q", got, want) + } }