|
| 1 | +# Claude Code Memory Plugin |
| 2 | + |
| 3 | +Long-term semantic memory for [Claude Code](https://docs.claude.com/en/docs/claude-code/overview). Recall happens automatically before every prompt and capture happens automatically after every turn — no MCP tool calls required from the model. |
| 4 | + |
| 5 | +Source: [examples/claude-code-memory-plugin](https://github.com/volcengine/OpenViking/tree/main/examples/claude-code-memory-plugin) |
| 6 | + |
| 7 | +## Quick Start |
| 8 | + |
| 9 | +### One-line installer (recommended) |
| 10 | + |
| 11 | +```bash |
| 12 | +bash <(curl -fsSL https://raw.githubusercontent.com/volcengine/OpenViking/main/examples/claude-code-memory-plugin/setup-helper/install.sh) |
| 13 | +``` |
| 14 | + |
| 15 | +The script runs on macOS and Linux. It checks dependencies, asks whether you'll connect to a **self-hosted** server or to **Volcengine OpenViking Cloud** (`https://api.vikingdb.cn-beijing.volces.com/openviking`), sets up `~/.openviking/ovcli.conf` (prompting only if missing), clones the OpenViking repo to `~/.openviking/openviking-repo`, adds the `claude` function wrapper to your shell rc, and installs the plugin via `claude plugin install`. Every step is idempotent — re-running is safe. |
| 16 | + |
| 17 | +If you'd rather do it by hand, follow the three steps below. |
| 18 | + |
| 19 | +### Manual setup |
| 20 | + |
| 21 | +#### 1. Wrap `claude` to inject env from `ovcli.conf` |
| 22 | + |
| 23 | +This is the recommended path. The plugin's hooks **and** the bundled MCP server both read env vars, so we set them once — but scoped to the `claude` invocation only, not exported globally. Append to `~/.zshrc` or `~/.bashrc`: |
| 24 | + |
| 25 | +```bash |
| 26 | +claude() { |
| 27 | + if [ -f ~/.openviking/ovcli.conf ]; then |
| 28 | + OPENVIKING_URL=$(jq -r '.url' ~/.openviking/ovcli.conf) \ |
| 29 | + OPENVIKING_API_KEY=$(jq -r '.api_key' ~/.openviking/ovcli.conf) \ |
| 30 | + command claude "$@" |
| 31 | + else |
| 32 | + command claude "$@" |
| 33 | + fi |
| 34 | +} |
| 35 | +``` |
| 36 | + |
| 37 | +Re-source your rc and verify (use `~/.bashrc` if you're on bash): |
| 38 | + |
| 39 | +```bash |
| 40 | +source ~/.zshrc # or: source ~/.bashrc |
| 41 | +type claude # expect: claude is a shell function |
| 42 | +``` |
| 43 | + |
| 44 | +Inside Claude Code, run `/mcp` after the next start — the OpenViking entry should show your remote URL with valid auth. |
| 45 | + |
| 46 | +> **Don't have `ovcli.conf` yet?** See the [CLI section of the Deployment Guide](../guides/03-deployment.md#cli) to set one up. |
| 47 | +> |
| 48 | +> **Pure local mode** (`http://127.0.0.1:1933`, no auth)? Skip this step — the plugin uses the local default silently. |
| 49 | +> |
| 50 | +> **Why a function instead of `export`?** Globally exported env vars leak into every child process spawned from your shell — npm scripts, build tools, crash dumps, `/proc/<pid>/environ`. The function-wrapper limits the secret to the `claude` process tree only. |
| 51 | +
|
| 52 | +#### 2. Install the plugin |
| 53 | + |
| 54 | +From the OpenViking repo root: |
| 55 | + |
| 56 | +```bash |
| 57 | +claude plugin marketplace add "$(pwd)/examples" --scope local |
| 58 | +claude plugin install claude-code-memory-plugin@openviking-plugins-local --scope local |
| 59 | +``` |
| 60 | + |
| 61 | +> Local install points Claude Code at the source directory. Edits to `scripts/`, `hooks/`, and config files take effect on the next hook invocation — no reinstall. But moving / renaming / deleting the source dir, or `git checkout`-ing to a branch without these files, breaks the plugin. |
| 62 | +
|
| 63 | +#### 3. Start Claude Code |
| 64 | + |
| 65 | +```bash |
| 66 | +claude |
| 67 | +``` |
| 68 | + |
| 69 | +Inside Claude Code, run `/mcp` to confirm the OpenViking MCP entry shows your remote URL. If the plugin doesn't seem to fire, set `OPENVIKING_DEBUG=1` and check `~/.openviking/logs/cc-hooks.log`. |
| 70 | + |
| 71 | +## Why a function wrapper? |
| 72 | + |
| 73 | +The plugin's hooks read `ovcli.conf` directly — but the bundled `.mcp.json` entry **cannot**. Claude Code parses `.mcp.json` itself and only supports `${VAR}` substitution, so config-file values can't transparently reach the MCP server URL or auth headers. |
| 74 | + |
| 75 | +Injecting env vars at `claude` invocation is the single path that covers both hooks and MCP. Wrapping in a shell function (rather than a global `export`) keeps the API key out of every other shell child process — see the security note in [the manual setup step 1](#1-wrap-claude-to-inject-env-from-ovcli-conf). |
| 76 | + |
| 77 | +**Symptom of misconfiguration**: hooks (auto-recall, auto-capture) work fine because they read `ovcli.conf` via Node, but the on-demand MCP tools (`search`, `read`, `store`, …) silently connect to `http://127.0.0.1:1933` with empty auth headers, and `/mcp` shows the wrong URL. |
| 78 | + |
| 79 | +## Configuration |
| 80 | + |
| 81 | +### Resolution priority |
| 82 | + |
| 83 | +Every plugin field follows this chain (highest → lowest): |
| 84 | + |
| 85 | +1. **Environment variables** (`OPENVIKING_*`) |
| 86 | +2. **`ovcli.conf`** — connection fields only (`url`, `api_key`, `account`, `user`, `agent_id`) |
| 87 | +3. **`ov.conf`** — server config; the plugin reads `server.url`, `server.root_api_key`, and a legacy `claude_code` block if present |
| 88 | +4. **Built-in defaults** (`http://127.0.0.1:1933`, no auth) |
| 89 | + |
| 90 | +> ⚠️ **Hooks only.** This chain is implemented in `scripts/config.mjs` and consumed by hook scripts. It does **not** apply to MCP server registration — see [Why a function wrapper?](#why-a-function-wrapper) above. |
| 91 | +
|
| 92 | +### Key environment variables |
| 93 | + |
| 94 | +| Env Var | Default | Description | |
| 95 | +|--------------------------------------------------|---------------|-----------------------------------------------------------------------| |
| 96 | +| `OPENVIKING_URL` / `OPENVIKING_BASE_URL` | — | Full server URL | |
| 97 | +| `OPENVIKING_API_KEY` / `OPENVIKING_BEARER_TOKEN` | — | API key; sent as `Authorization: Bearer <key>` | |
| 98 | +| `OPENVIKING_AUTO_RECALL` | `true` | Enable auto-recall on every user prompt | |
| 99 | +| `OPENVIKING_RECALL_LIMIT` | `6` | Max memories to inject per turn | |
| 100 | +| `OPENVIKING_RECALL_TOKEN_BUDGET` | `2000` | Token budget for inline content; over-budget items degrade to URI hints | |
| 101 | +| `OPENVIKING_AUTO_CAPTURE` | `true` | Enable auto-capture; also gates write hooks | |
| 102 | +| `OPENVIKING_BYPASS_SESSION` | `false` | One-shot: `1`/`true` skips every hook in the current process | |
| 103 | +| `OPENVIKING_BYPASS_SESSION_PATTERNS` | `""` | CSV of glob patterns matched against `session_id` or `cwd` | |
| 104 | +| `OPENVIKING_MEMORY_ENABLED` | (auto) | `0`/`false`=force off; `1`/`true`=force on | |
| 105 | +| `OPENVIKING_DEBUG` | `false` | Write hook logs to `~/.openviking/logs/cc-hooks.log` | |
| 106 | + |
| 107 | +For multi-tenant deployments, `OPENVIKING_ACCOUNT`, `OPENVIKING_USER`, and `OPENVIKING_AGENT_ID` set the corresponding `X-OpenViking-*` headers. The full env-var list (recall tuning, capture tuning, lifecycle, debug) is in the [plugin README](https://github.com/volcengine/OpenViking/blob/main/examples/claude-code-memory-plugin/README.md#configuration). |
| 108 | + |
| 109 | +### Bypass a session |
| 110 | + |
| 111 | +Use Claude Code in a `/tmp` PoC directory without polluting your long-term memory: |
| 112 | + |
| 113 | +```bash |
| 114 | +# Persistent: any session whose session_id or cwd matches a pattern |
| 115 | +export OPENVIKING_BYPASS_SESSION_PATTERNS='/tmp/**,**/scratch/**,/Users/me/Dev/throwaway/*' |
| 116 | + |
| 117 | +# Or one-shot: |
| 118 | +OPENVIKING_BYPASS_SESSION=1 claude |
| 119 | +``` |
| 120 | + |
| 121 | +When bypass is active, every hook approves immediately without contacting OpenViking. |
| 122 | + |
| 123 | +## Compared to Claude Code's built-in `MEMORY.md` |
| 124 | + |
| 125 | +This plugin **complements** Claude Code's native memory system, it doesn't replace it: |
| 126 | + |
| 127 | +| Feature | Built-in `MEMORY.md` | OpenViking plugin | |
| 128 | +|--------------|-----------------------------------|----------------------------------------------------| |
| 129 | +| Storage | Flat markdown | Vector DB + structured extraction | |
| 130 | +| Search | Loaded into context wholesale | Semantic similarity + ranking + token budget | |
| 131 | +| Scope | Per-project | Cross-project, cross-session, cross-agent | |
| 132 | +| Capacity | ~200 lines (context limit) | Unlimited (server-side storage) | |
| 133 | +| Extraction | Manual rules | LLM-powered entity / preference / event extraction | |
| 134 | +| Subagents | Same as parent | Isolated session + typed agent namespace | |
| 135 | + |
| 136 | +## Hook behavior |
| 137 | + |
| 138 | +| Hook | Trigger | Action | |
| 139 | +|-----------------------|--------------------------------------------|---------------------------------------------------------------------------------------------------| |
| 140 | +| `UserPromptSubmit` | Each user turn | Search OV → rank → inject `<openviking-context>` block within a token budget | |
| 141 | +| `Stop` | Claude finishes a response | Parse transcript → push new user turns to OV session → commit when pending tokens cross threshold | |
| 142 | +| `SessionStart` | New / resumed / post-compact session | On `resume`/`compact`, fetch the latest archive overview and inject it as additional context | |
| 143 | +| `PreCompact` | Before Claude Code rewrites the transcript | Commit pending messages so they become an archive before CC mutates the transcript | |
| 144 | +| `SessionEnd` | Claude Code session closes | Final commit so the last window is archived | |
| 145 | +| `SubagentStart` | Parent spawns a subagent via Task tool | Derive an isolated OV session ID for the subagent, persist start state | |
| 146 | +| `SubagentStop` | Subagent finishes | Read subagent transcript → push to isolated session with subagent-typed agent header → commit | |
| 147 | + |
| 148 | +`Stop`, `SessionEnd`, and `SubagentStop` use a detached-worker pattern so the user never waits for OpenViking. Disable with `OPENVIKING_WRITE_PATH_ASYNC=false` if you need deterministic ordering. |
| 149 | + |
| 150 | +`auto-capture` strips `<openviking-context>`, `<system-reminder>`, `<relevant-memories>`, and `[Subagent Context]` blocks before pushing to OV — without this, the recall context the plugin injects this turn would be captured back as part of the user's message next turn. |
| 151 | + |
| 152 | +## Troubleshooting |
| 153 | + |
| 154 | +| Symptom | Cause | Fix | |
| 155 | +|--------------------------------------------|----------------------------------------------------------------|----------------------------------------------------------------------------------------------| |
| 156 | +| Plugin not activating | No `ov.conf` / `ovcli.conf` found | Run the [one-line installer](#one-line-installer-recommended), or set `OPENVIKING_MEMORY_ENABLED=1` plus URL/API_KEY env vars | |
| 157 | +| Hooks fire but recall is empty | OpenViking server not running, or wrong URL | `curl "$(jq -r '.url' ~/.openviking/ovcli.conf)/health"` | |
| 158 | +| MCP tools hit `127.0.0.1` instead of remote | `.mcp.json` only resolves `${VAR}`, no `ovcli.conf` integration | See [Why a function wrapper?](#why-a-function-wrapper) | |
| 159 | +| Remote auth 401 / 403 | Wrong API key or missing tenant headers | Verify `OPENVIKING_API_KEY`; for multi-tenant, also check `OPENVIKING_ACCOUNT` / `OPENVIKING_USER` | |
| 160 | +| `Stop` hook times out | Server slow + sync write path | Leave `OPENVIKING_WRITE_PATH_ASYNC=true` (default), or raise the `Stop` timeout in `hooks/hooks.json` | |
| 161 | + |
| 162 | +## See also |
| 163 | + |
| 164 | +- [Plugin README](https://github.com/volcengine/OpenViking/blob/main/examples/claude-code-memory-plugin/README.md) — exhaustive env-var tables, hook timeouts, debug logging, architecture diagram |
| 165 | +- [Migration notes](https://github.com/volcengine/OpenViking/blob/main/examples/claude-code-memory-plugin/MIGRATION.md) — upgrading from earlier plugin versions |
| 166 | +- [MCP Integration Guide](../guides/06-mcp-integration.md) — for MCP tool parameters and other clients |
| 167 | +- [Deployment Guide → CLI](../guides/03-deployment.md#cli) — `ovcli.conf` setup |
0 commit comments