@ephemushroom/opencode-claude-mem 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,625 +1,158 @@
1
1
  # opencode-claude-mem
2
2
 
3
- Persistent memory for [OpenCode](https://opencode.ai), powered by
3
+ The **Memory sidebar** for [OpenCode](https://opencode.ai), backed by
4
4
  [Claude-Mem](https://github.com/thedotmack/claude-mem).
5
5
 
6
- **Supports OpenCode 1 and OpenCode 2 with the same package name from v0.5.0.**
7
- No `/v2` or `/cli` suffix is required. Existing version-specific entries remain compatible.
8
-
9
- **v0.6.1** updates the OpenCode 2 TUI adapter for the `@opencode/plugin` 2.0.16
10
- plugin contract. **v0.6.0** adds per-turn memory tracking, `mem-save`, file history and opt-in semantic
11
- injection, and fixes the OpenCode 2 sidebar's `No renderer found` crash.
12
-
13
- Share the same Claude-Mem worker, database, and memory across your coding
14
- agents: memories written by Claude Code are visible to OpenCode, and vice
15
- versa. Previous observations and summaries are injected into new OpenCode
16
- sessions automatically.
17
-
18
- ```mermaid
19
- flowchart LR
20
- CC[Claude Code] -->|writes as<br/>platform_source=claude| W
21
- OC[OpenCode<br/>+ this plugin] -->|writes as<br/>platform_source=opencode| W
22
- W[Claude-Mem Worker<br/>127.0.0.1:37777] --> DB[(SQLite +<br/>ChromaDB)]
23
- W -->|shared project memory| CC
24
- W -->|shared project memory| OC
25
- ```
26
-
27
- > **Note:** This plugin is a thin OpenCode adapter for an existing Claude-Mem
28
- > installation. It does **not** install Claude-Mem, manage slash commands, or
29
- > register Claude Code MCP servers for you.
30
-
31
- ## Quick Start
32
-
33
- 1. Install and configure Claude-Mem in Claude Code.
34
- 2. Add this plugin to your `opencode.json` — pick the section matching your
35
- OpenCode version:
36
-
37
- ### OpenCode 1
38
-
39
- ```json
40
- {
41
- "plugin": ["@ephemushroom/opencode-claude-mem"]
42
- }
43
- ```
44
-
45
- ### OpenCode 2
46
-
47
- ```json
48
- {
49
- "plugins": ["@ephemushroom/opencode-claude-mem"]
50
- }
51
- ```
52
-
53
- 3. Restart OpenCode.
54
- 4. Start a session — memory context is injected automatically, the `mem-*`
55
- tools become available, and a collapsible **Memory** section appears in the
56
- sidebar (both versions).
57
-
58
- Using [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent)?
59
- See [Disable the Claude Code bridge](#using-with-oh-my-openagent-disable-the-claude-code-bridge)
60
- to avoid running two claude-mem integrations at once.
61
-
62
- ## OpenCode 1 and 2
63
-
64
- OpenCode 1 and OpenCode 2 install side by side (`opencode` vs `opencode2`) and
65
- their plugin APIs are incompatible. This package exposes **one bare package
66
- name for both runtimes**, with thin bridges that select the existing adapter
67
- contract. No `/v2` or `/cli` suffix is needed:
68
-
69
- | | OpenCode 1 | OpenCode 2 |
70
- |---|---|---|
71
- | **Binary** | `opencode` | `opencode2` |
72
- | **Server plugin config** | `opencode.json` → `plugin` | `opencode.json` → `plugins` |
73
- | **Server plugin entry** | `@ephemushroom/opencode-claude-mem` | `@ephemushroom/opencode-claude-mem` |
74
- | **Sidebar config** | `~/.config/opencode/tui.json` → `plugin` (self-healed) | `~/.config/opencode/cli.json` → `plugins` (self-healed) |
75
- | **Sidebar config entry** | `@ephemushroom/opencode-claude-mem` | `@ephemushroom/opencode-claude-mem` |
76
- | **Sidebar resolution** | package `./tui` export, `tui()` | package `./tui` export, `setup()` |
77
-
78
- Both adapters talk to the same Claude-Mem worker, so memory written from V1,
79
- V2, and Claude Code is shared. The V2 entrypoint covers the same behavior —
80
- context injection, tool observation capture, the `mem-search`/`mem-timeline`/
81
- `mem-get-observations`/`mem-save` tools, per-user-message registration and session summarize/cleanup via the event
82
- stream.
83
-
84
- The bare package is tested with `opencode 1.18.29` and
85
- `opencode2 0.0.0-beta-19151`; v0.6.0 also passes server and real
86
- sidebar interaction checks on `0.0.0-beta-19192`. The V2 SDK is pinned because preview APIs can
87
- change. Unified routing requires **v0.5.0 or later**; the older `0.4.4` package
88
- does not provide it. Upgrade the package in both server and sidebar configurations.
89
- Existing `/server`, `/tui`, `/v2`, and `/cli` exports remain supported; replace
90
- an old config entry rather than adding the bare name beside it.
91
-
92
- After upgrading, finish active tasks, close the client, and restart the V2
93
- background service with `opencode2 service restart` before reopening `opencode2`.
94
- For V1, restart `opencode`. Verify server activation with `opencode2 plugin list`.
95
-
96
- In V2, installing the server plugin
97
- (`"plugins": ["@ephemushroom/opencode-claude-mem"]`) auto-appends the same
98
- bare package to `~/.config/opencode/cli.json` on first load. Restart OpenCode
99
- after that first load if the sidebar is not yet present. Beta `19151` still
100
- uses a separate CLI config; this is not a promise of same-launch discovery
101
- from the server config alone. To register manually, use:
102
-
103
- ```json
104
- {
105
- "plugins": ["@ephemushroom/opencode-claude-mem"]
106
- }
107
- ```
108
-
109
- ## What You Get
110
-
111
- | Surface | What it looks like |
112
- |---|---|
113
- | **System prompt** | `<claude-mem-context>` block with recent observations + session summaries for the current project |
114
- | **Tools** | `mem-search` → `mem-timeline` → `mem-get-observations`, plus explicit `mem-save` (no MCP server needed) |
115
- | **Read history** | Bounded supplementary file history after eligible file reads; original file output is preserved |
116
- | **Optional semantic context** | Relevant project memories for the current real user prompt; **off by default** |
117
- | **Sidebar** | `▶ Memory (online, 11.7k obs)` — click to expand recent sessions and latest observations |
118
- | **Background** | Every tool call, assistant message, and file edit captured as observations; sessions summarized on idle |
119
-
120
- ### Sidebar
121
-
122
- The plugin registers a sidebar section styled after OpenCode's native
123
- MCP/Context sections — borderless, click-to-toggle:
124
-
125
6
  ```text
126
- ▶ Memory (online, 11.7k obs) ← collapsed (default), single line
127
- ```
7
+ ▶ Memory (online, 549 obs) ← collapsed (default): this project's observations
128
8
 
129
- ```text
130
- ▼ Memory ← click header to expand
131
- • obs 11.7k · sum 2311 · ses 1186
9
+ ▼ Memory opencode-claude-mem ← click the header to expand
10
+ • obs 549 · sum 51 · ses 13
11
+ • all 54.0k obs ← every project, muted
132
12
  Recent sessions
133
- • 实现 tui.json 自动注册(self-heal)机制…
134
- • 设计 Memory 面板:跨工具记忆共享…
13
+ • 发布 0.7.0 前核查与侧边栏项目统计…
14
+ 12m ago
135
15
  Latest
136
- ◆ Memory 侧边栏完整通过 CI 流水线…
137
- ● 修复点击不展开:改用 solid-js signal…
138
- ⚖ 计划按原生风格重构 Memory 面板…
139
- ```
140
-
141
- - Summary line turns **yellow** while the worker is processing (`(queue N)`)
142
- and **red** when offline (`(offline)`).
143
- - Observation icons match the injected context legend: ◆ feature · ● bugfix ·
144
- ⚖ decision · ○ discovery · ↻ refactor · ✓ change · ⚠/⚷ security.
145
- - Recent items are only fetched while expanded, keeping the collapsed poll
146
- loop cheap (stats every 5s).
147
- - Fails open: worker offline → shows the offline state, never blocks the TUI.
148
-
149
- The sidebar loads via the package's `./tui` bridge in both versions (OpenCode 2
150
- registers the bare name in `~/.config/opencode/cli.json`). On startup
151
- the plugin **self-heals the TUI config**: if it is registered as a server
152
- plugin but missing from the TUI plugin list, it appends itself — no manual
153
- configuration. OpenCode 1 writes into `tui.json` (symlinked files are written
154
- through, preserving dotfiles setups); OpenCode 2 appends
155
- `@ephemushroom/opencode-claude-mem` to `cli.json`'s `plugins` array.
156
- Existing bare, `/cli`, or `/tui` sidebar entries (including versions and
157
- object-form options) are left untouched, without adding another entry.
158
- Malformed or comment-bearing CLI config is left untouched; add the bare name
159
- manually in that case.
160
-
161
- ### Memory Tools
162
-
163
- Three native OpenCode tools cover the same 3-step search workflow as the upstream
164
- Claude-Mem MCP server — no MCP server or stdio subprocess required:
165
-
166
- ```mermaid
167
- flowchart LR
168
- A["mem-search<br/><i>find IDs by query</i>"] --> B["mem-timeline<br/><i>context around an ID</i>"]
169
- B --> C["mem-get-observations<br/><i>full details for IDs</i>"]
170
- ```
171
-
172
- | Tool | Worker endpoint | Use it for |
173
- |---|---|---|
174
- | `mem-search` | `GET /api/search` | Formatted index with query, project, source, type, date, pagination, and ordering filters |
175
- | `mem-timeline` | `GET /api/timeline` | Chronological records around an `anchor` ID (or auto-located via `query`) |
176
- | `mem-get-observations` | `POST /api/observations/batch` | Full details for IDs — e.g. the IDs shown in the injected context |
177
- | `mem-save` | `POST /api/memory/save` | Explicitly save required `text`, optional `title` and `project` (defaults to current project) |
178
-
179
- `mem-save` strips `<private>` and `<claude-mem-context>` material from text/title,
180
- rejects empty/fully stripped content and values above 24 KiB, and reports success
181
- only for a valid Worker acknowledgement. It never retries automatically: after
182
- a timeout, check with search before retrying because the write may have succeeded.
183
- Manual saves do not require an observation-model inference pass, but the Worker
184
- still performs asynchronous vector indexing; this is **not** a promise of zero
185
- embedding cost. Native and namespaced `mem-save` calls are excluded from automatic
186
- observation capture.
187
-
188
- ### Memory configuration (both runtimes)
189
-
190
- The per-turn registration, `mem-save`, file history, and semantic options described
191
- here require **v0.6.0 or later**. Upgrade both server and sidebar package entries;
192
- `0.5.0` does not include these features or the renderer fix.
193
-
194
- Configuration is read once when the server plugin is set up. Quit and restart
195
- OpenCode after changing options, environment, or settings; on V2 also restart the
196
- background service. No semantic feature is enabled globally by this plugin.
197
-
198
- **OpenCode 1** uses a tuple in `opencode.json`:
16
+ ◆ TUI 动态坐标重算修复后连续 4 次…
17
+ 3m ago · #55670
18
+ Click an item for details ← opens the full summary or observation
19
+ ```
20
+
21
+ From **v0.7.0** this package is sidebar only. Memory capture, context injection, search
22
+ and the MCP tools come from the **official claude-mem OpenCode plugin**, which tracks the
23
+ Worker's API in the same release. Earlier versions shipped their own capture adapter; it is
24
+ removed so the two can never run side by side.
25
+
26
+ - Counts are for the current project (the directory name), matching the Worker's own
27
+ project filter: case-insensitive, merged projects included. They are read from the Worker's
28
+ database in read-only mode, because its HTTP API reports only global totals. When the
29
+ database is not on this machine or cannot be read, the sidebar shows global totals and
30
+ labels them `all ·`.
31
+ - Titles are truncated by display width with an ellipsis, so wide CJK text fits; the
32
+ project's own name is dropped from titles, since every row belongs to it.
33
+ - Clicking a recent session or observation opens its details in a dialog; observations show
34
+ the `get_observations([id])` call that fetches them through MCP.
35
+ - Summary turns yellow while the Worker is processing (`(queue N)`) and red when offline.
36
+ - Icons follow the injected context legend: ◆ feature · ● bugfix · ⚖ decision ·
37
+ ○ discovery · ↻ refactor · ✓ change · ⚠/⚷ security.
38
+ - Recent rows are fetched only while expanded; collapsed, it polls stats every 5s.
39
+ - Read-only and fail-open: an offline Worker shows the offline state, never blocks the TUI.
40
+
41
+ Tested with OpenCode **2.2.7** (`@opencode/plugin` 2.2.7, `@opentui/solid` 0.8.1). The V1
42
+ sidebar contract is kept on the same `./tui` export.
43
+
44
+ ## Setup (OpenCode 2)
45
+
46
+ 1. **Install Claude-Mem** in Claude Code and make sure the Worker is healthy:
47
+
48
+ ```bash
49
+ curl -s http://127.0.0.1:37777/api/health
50
+ ```
51
+
52
+ 2. **Install the official server plugin.** OpenCode 2 auto-loads single files from
53
+ `~/.config/opencode/plugins/`; the file ships in the `claude-mem` npm package:
54
+
55
+ ```bash
56
+ mkdir -p ~/.config/opencode/plugins
57
+ cd "$(mktemp -d)" && npm pack claude-mem@latest --silent \
58
+ && tar -xzf claude-mem-*.tgz package/dist/opencode-plugin/index.js \
59
+ && cp package/dist/opencode-plugin/index.js ~/.config/opencode/plugins/claude-mem.js
60
+ ```
61
+
62
+ Claude-Mem's auto-update upgrades the Worker, not this copy: repeat this step after a
63
+ Worker upgrade.
64
+
65
+ 3. **Register the MCP server** (search, timeline, observations, `save_memory`,
66
+ `work_state_*`) in `~/.config/opencode/opencode.json`, under V2's `mcp.servers`:
67
+
68
+ ```json
69
+ {
70
+ "mcp": {
71
+ "servers": {
72
+ "claude-mem": {
73
+ "type": "local",
74
+ "command": [
75
+ "/opt/homebrew/bin/node",
76
+ "/Users/<you>/.claude/plugins/marketplaces/thedotmack/plugin/scripts/mcp-server.cjs"
77
+ ]
78
+ }
79
+ }
80
+ }
81
+ }
82
+ ```
83
+
84
+ Point at the marketplace path, not a versioned `plugins/cache/.../<version>/` path, so
85
+ Worker upgrades keep it valid. Prefer this over `npx claude-mem install --ide=opencode`,
86
+ which writes the V1 `mcp` shape.
87
+
88
+ 4. **Add the sidebar** to `~/.config/opencode/cli.json`:
89
+
90
+ ```json
91
+ { "plugins": ["@ephemushroom/opencode-claude-mem"] }
92
+ ```
93
+
94
+ 5. **Remove this package from `opencode.json` `plugins`** if an older version put it there.
95
+ Both server plugins use the id `claude-mem`; keeping both captures every tool twice.
96
+
97
+ ### Share memory across Claude Code and OpenCode
98
+
99
+ The official plugin injects only OpenCode-recorded memory by default. To see every source,
100
+ add this to `~/.claude-mem/settings.json` (it also applies to Claude Code's session start):
199
101
 
200
102
  ```json
201
- {
202
- "plugin": [["@ephemushroom/opencode-claude-mem", {
203
- "semanticInjection": { "enabled": true, "limit": 5, "maxChars": 6000, "timeoutMs": 2000 },
204
- "fileContext": { "enabled": true, "limit": 15, "maxChars": 6000, "timeoutMs": 1500 }
205
- }]]
206
- }
103
+ { "CLAUDE_MEM_SESSION_START_INCLUDE_ALL_SOURCES": "true" }
207
104
  ```
208
105
 
209
- **OpenCode 2** uses a package/options object (not a V1 tuple):
210
-
211
- ```json
212
- {
213
- "plugins": [{
214
- "package": "@ephemushroom/opencode-claude-mem",
215
- "options": {
216
- "semanticInjection": { "enabled": true, "limit": 5, "maxChars": 6000, "timeoutMs": 2000 },
217
- "fileContext": { "enabled": true, "limit": 15, "maxChars": 6000, "timeoutMs": 1500 }
218
- }
219
- }]
220
- }
221
- ```
222
-
223
- | Option | Semantic default | File-history default | Accepted values |
224
- |---|---|---|---|
225
- | `enabled` | `false` | `true` | Boolean |
226
- | `limit` | `5` | `15` | Integer 1–50 |
227
- | `maxChars` | `6000` | `6000` | Integer 256–24000; supplementary payload only |
228
- | `timeoutMs` | `2000` | `1500` | Integer 100–10000 |
229
-
230
- Per-field precedence for semantic `enabled` and `limit` is **explicit plugin
231
- options > environment > `~/.claude-mem/settings.json` > defaults**. The environment
232
- and settings keys are `CLAUDE_MEM_SEMANTIC_INJECT` (`true`/`false` or `1`/`0`) and
233
- `CLAUDE_MEM_SEMANTIC_INJECT_LIMIT` (integer 1–50). Settings may use booleans/numbers
234
- or their string equivalents. Explicit `false` overrides environment `true`.
235
- Invalid values fall through to the next valid source; other controls use explicit
236
- options then defaults. Settings are read-only and never logged.
237
-
238
- Semantic retrieval uses `POST /api/context/semantic` with `{q, project, limit}`
239
- and reads JSON `{context, count}`. `q` is the sanitized real current prompt,
240
- at least 20 characters; media-only, fully private, and identifiable internal
241
- messages are excluded. No `platformSource` filter is sent, so Claude Code and
242
- OpenCode memories remain shared. Each real prompt identity gets at most one
243
- retrieval attempt, including failure/timeout, reused across tool-loop dispatches.
244
- The system block is replaced rather than accumulated. Old Workers, malformed
245
- responses and offline retrieval fail open.
246
-
247
- File history uses `GET /api/observations/by-file` with repeated `path` parameters
248
- (absolute and project-relative, normalized to forward slashes), `projects` and `limit`. Supported read arguments
249
- include V1 `filePath`, V2 `path`, and `file_path`/`filePaths`/`file_paths` aliases.
250
- Only existing regular files of at least 1500 bytes qualify, at most 10 paths per
251
- read. Lookups run concurrently within one configured per-read deadline (including
252
- file metadata lookup), not ten serial timeout periods. Rows are deduplicated by
253
- memory session and the displayed text is bounded; a file whose mtime is at least
254
- the newest observation timestamp gets no history. Delivered history is deduplicated per
255
- session/file revision (mtime and size); failures and timeouts may retry on a later read.
256
- Editing the file or starting a new session also permits another lookup. Supplementary history is explicitly
257
- labeled and wrapped in memory tags, appended without replacing/truncating the
258
- original read result, and never included in the automatic tool observation.
259
- Known child sessions (`parentID` when supplied by the host) skip file-history
260
- enrichment; V1 resumed sessions without that metadata cannot reliably be identified
261
- as subagents and are not guessed from the agent's name.
262
-
263
- ### Prompt lifecycle and Worker limitations
264
-
265
- V1 registers stable `chat.message` IDs; V2 records admission IDs without writing,
266
- then registers only IDs present in delivered `session.context` records at model
267
- dispatch. Thus queued V2 prompts do not advance the Worker's current prompt.
268
- Resumed sessions without local admission history use the latest delivered real
269
- user turn, not a synthetic `SESSION_START`, and tool callbacks do not reread the
270
- whole history. Registrations are serialized per session and successful IDs are
271
- deduplicated locally; a failed registration retains its real prompt for retry.
272
- Media-only turns use `[media prompt]`. Fully private or identifiable synthetic
273
- turns suppress automatic observations, summaries and semantic retrieval locally,
274
- including when the Worker reports a skipped/private registration.
275
-
276
- **Upstream v13.24.1 still deduplicates identical prompt text within its own time
277
- window and has no consumed external prompt-ID contract.** This plugin submits
278
- distinct IDs even if their text is identical, without prefixing/changing the text,
279
- but cannot guarantee distinct database prompt rows. A `duplicate` acknowledgement
280
- is accepted; other skipped registrations do not authorize automatic capture.
281
- V1 exposes submission callbacks rather than V2's delivered/inbox distinction;
282
- it cannot provide the same queued-consumption guarantee. A restart cannot restore
283
- the plugin's in-memory ID dedupe ledger, so upstream dedupe remains relevant.
284
-
285
- Base context is session-isolated, limited to 24000 characters, with a two-second
286
- request timeout. It is refreshed at V1 compaction start/end and V2 compaction
287
- requests/completed events or changed compaction records. Failed reads are retryable;
288
- an invalidated or deleted session cannot be repopulated by an older in-flight read.
289
-
290
- ## How It Works
291
-
292
- ```mermaid
293
- sequenceDiagram
294
- participant OC as OpenCode
295
- participant P as Plugin
296
- participant W as Claude-Mem Worker
297
-
298
- OC->>P: plugin loads
299
- P->>P: self-heal tui.json / cli.json
300
- P->>W: health check (auto-start via bunx if down)
301
- OC->>P: each real user prompt (chat.message / delivered context)
302
- P->>W: POST /api/sessions/init
303
- OC->>P: system prompt transform / context hook
304
- W-->>P: GET /api/context/inject (cached per session)
305
- P-->>OC: inject <claude-mem-context> into system prompt
306
- loop during the session
307
- OC->>P: tool.execute.after / assistant text / file.edited
308
- P->>W: POST /api/sessions/observations
309
- end
310
- OC->>P: session idle / execution succeeded / compaction ended
311
- P->>W: POST /api/sessions/summarize
312
- OC->>P: session.deleted
313
- P->>P: flush pending observations and release local state
314
- ```
315
-
316
- The plugin is intentionally small: it only adapts OpenCode hook events to the
317
- Claude-Mem worker HTTP API. All indexing, summarization, memory search, and
318
- storage stay in upstream Claude-Mem.
319
-
320
- ### Hook Mapping (V1 ↔ V2)
321
-
322
- The V1 and V2 entrypoints adapt the same behavior onto two different plugin
323
- APIs:
324
-
325
- | Behavior | OpenCode 1 | OpenCode 2 |
326
- |---|---|---|
327
- | Inject memory context | `experimental.chat.system.transform` | `ctx.session.hook('context')` |
328
- | Refresh memory on compaction | `experimental.session.compacting` + `session.compacted` | compaction model requests/events + delivered compaction records |
329
- | Register each real user prompt | `chat.message` (message ID) | `ctx.session.hook('context')` (`ctx.session.context` delivered IDs; not inbox admission) |
330
- | Capture tool observations | `tool.execute.after` | `ctx.tool.hook('execute.after')` |
331
- | Custom memory tools | `tool` (`mem-search`, …) | `ctx.tool.transform` (`tools.add`) |
332
- | Assistant text capture | `event` (`message.updated`, debounced 250ms) | `session.text.ended` event (complete text) |
333
- | File edit observations | `event` (`file.edited`) | `ctx.tool.hook('execute.after')` |
334
- | Summarize on idle | `event` (`session.idle`) | `session.execution.succeeded` event |
335
- | Summarize after compaction | `event` (`session.compacted`) | `session.compaction.ended` event |
336
- | Release session tracking | `event` (`session.deleted`) | `session.deleted` event |
337
- | Sidebar slot | `api.slots.register` (`sidebar_content`) | `ctx.ui.slot({ append: 'sidebar.content', render })` |
338
-
339
- ### Cross-Tool Memory Sharing
340
-
341
- Writes are attributed (`platformSource: "opencode"`), reads are shared:
342
-
343
- | Operation | Behavior |
344
- |---|---|
345
- | OpenCode writes | Stored as `platform_source=opencode` |
346
- | Claude Code writes | Stored as `platform_source=claude` |
347
- | Either reads (inject/search) | Sees **all** memory for the project, regardless of source |
348
-
349
- The sharing key is the **project name** (worktree directory name) — work in
350
- the same project directory and memory flows both ways.
351
-
352
- ## Installation
353
-
354
- ### Prerequisites
355
-
356
- - [Claude Code](https://claude.com/claude-code) with
357
- [Claude-Mem](https://github.com/thedotmack/claude-mem) installed
358
- - [OpenCode](https://opencode.ai) with plugin support
359
- - A running Claude-Mem worker (default `127.0.0.1:37777`)
360
-
361
- ### Step 1: Install Claude-Mem
362
-
363
- In Claude Code:
364
-
365
- ```text
366
- /plugin marketplace add thedotmack/claude-mem
367
- /plugin install claude-mem
368
- ```
369
-
370
- Restart Claude Code so the worker can start and initialize its data directory.
371
-
372
- ### Step 2: Add the OpenCode Plugin
373
-
374
- Add this plugin to your project or global `opencode.json`:
375
-
376
- **OpenCode 1** (`opencode`):
377
-
378
- ```json
379
- {
380
- "plugin": ["@ephemushroom/opencode-claude-mem"]
381
- }
382
- ```
383
-
384
- **OpenCode 2** (`opencode2`):
385
-
386
- ```json
387
- {
388
- "plugins": ["@ephemushroom/opencode-claude-mem"]
389
- }
390
- ```
391
-
392
- The two runtimes can be installed side by side and use the same package name.
393
- Their config keys remain different: `plugin` in V1, `plugins` in V2.
394
-
395
- Then restart OpenCode.
396
-
397
- ### Step 3: Verify
106
+ ### Verify
398
107
 
399
108
  ```bash
400
- curl -s http://127.0.0.1:37777/api/health
109
+ opencode plugin list # claude-mem local ~/.config/opencode/plugins/claude-mem.js
110
+ opencode mcp list # ✓ claude-mem connected
401
111
  ```
402
112
 
403
- If the worker is healthy, the sidebar shows `▶ Memory (online, … obs)`.
404
- OpenCode 1 also shows a `Memory active · <project>` toast when a session
405
- starts (OpenCode 2 only toasts a warning when the worker is offline).
406
-
407
- ## Using with oh-my-openagent: Disable the Claude Code Bridge
113
+ Open a session: the sidebar shows `▶ Memory (online, …)`, and new observations carry
114
+ canonical tool names (`Read`, `Edit`, `Bash`) with `files_modified` filled in.
408
115
 
409
- oh-my-openagent ships a Claude Code compatibility layer that can load Claude
410
- Code plugins — including `claude-mem@thedotmack` — inside OpenCode. Running
411
- that bridge **and** this native plugin at the same time means two integrations
412
- write to the same worker:
116
+ ## Migrating from v0.6.x
413
117
 
414
- - duplicate observations for every tool call
415
- - duplicate context injection and toasts
416
- - bridged MCP tools (`search`, `timeline`, `get_observations`) shadowing the
417
- native `mem-*` tools
118
+ v0.6.x registered `@ephemushroom/opencode-claude-mem` as a **server** plugin. Its capture,
119
+ `mem-*` tools, file-history enrichment and semantic injection are gone:
418
120
 
419
- Disable the bridge for claude-mem in `~/.config/opencode/oh-my-openagent.jsonc`:
420
-
421
- ```jsonc
422
- {
423
- "claude_code": {
424
- "plugins_override": {
425
- "claude-mem@thedotmack": false
426
- }
427
- }
428
- }
429
- ```
430
-
431
- This only disables the *bridged* claude-mem inside OpenCode. Claude Code
432
- itself keeps using claude-mem normally, and memory stays shared through the
433
- worker. Everything the bridge provided is covered natively by this plugin:
434
-
435
- | Bridged (before) | Native (this plugin) |
121
+ | v0.6.x | Now |
436
122
  |---|---|
437
- | MCP `search` | `mem-search` tool |
438
- | MCP `timeline` | `mem-timeline` tool |
439
- | MCP `get_observations` | `mem-get-observations` tool |
440
- | `SessionStart` context hook | `system.transform` injection |
441
- | `PostToolUse` observation hook | `tool.execute.after` capture |
442
-
443
- ## Reference
444
-
445
- ### Hook Mapping
446
-
447
- Claude Code hooks → this plugin's adapters (see [Hook Mapping (V1 ↔ V2)](#hook-mapping-v1--v2)
448
- for the per-runtime API):
449
-
450
- | Claude Code | OpenCode (both runtimes) | Purpose |
451
- |---|---|---|
452
- | `SessionStart` | system prompt injection (`system.transform` / `context` hook) | Inject memory context |
453
- | `SessionStart` | compaction injection (`session.compacting` / context hook) | Preserve memory during compaction |
454
- | `UserPromptSubmit` | session init (`chat.message` / context hook) | Initialize session with real user prompt |
455
- | `PostToolUse` | observation capture (`tool.execute.after` / `execute.after` hook) | Capture tool observations |
456
- | Claude-Mem MCP `search` | `mem-search` tool | Search memory from OpenCode |
457
- | Claude-Mem MCP `timeline` | `mem-timeline` tool | Chronological context around an observation |
458
- | Claude-Mem MCP `get_observations` | `mem-get-observations` tool | Fetch full observation details by ID |
459
- | _(streaming)_ | assistant text (`message.updated` / `session.text.ended`) | Capture assistant text |
460
- | _(streaming)_ | file edits (`file.edited` / tool hook) | Record file edit observations |
461
- | _(compaction)_ | `session.compacted` / `session.compaction.ended` | Summarize after OpenCode compacts |
462
- | `Stop` | `session.idle` / `session.execution.succeeded` | Flush + summarize |
463
- | `SessionEnd` | `session.deleted` | Flush observations and release local state; Worker self-completes |
464
-
465
- ### Worker API Endpoints Used
466
-
467
- | Method | Endpoint | Purpose |
468
- |---|---|---|
469
- | `GET` | `/api/health` | Health check |
470
- | `GET` | `/api/context/inject?project={name}` | Fetch formatted memory context |
471
- | `POST` | `/api/sessions/init` | Initialize session |
472
- | `POST` | `/api/sessions/observations` | Store tool observation |
473
- | `POST` | `/api/sessions/summarize` | Trigger summarization |
474
- | `GET` | `/api/search?query=...&project=...&dateStart=...&dateEnd=...` | `mem-search`; also supports `limit`, `platformSource`, `type`, `obs_type`, `offset`, and `orderBy` |
475
- | `GET` | `/api/timeline?project={name}&anchor={id}` | `mem-timeline` |
476
- | `POST` | `/api/observations/batch` | `mem-get-observations` |
477
- | `POST` | `/api/memory/save` | Explicit `mem-save`, nested `metadata.platformSource` attribution |
478
- | `POST` | `/api/context/semantic` | Opt-in semantic context; JSON `{context,count}` response |
479
- | `GET` | `/api/observations/by-file?path=...&path=...&projects=...&limit=...` | Supplementary file history |
480
- | `GET` | `/api/stats` + `/api/processing-status` | Sidebar status |
481
- | `GET` | `/api/summaries` + `/api/observations` | Sidebar recent items (expanded only) |
482
-
483
- The worker endpoint is resolved in this order: `CLAUDE_MEM_WORKER_HOST` /
484
- `CLAUDE_MEM_WORKER_PORT` environment variables → `~/.claude-mem/settings.json`
485
- → `127.0.0.1:37777`.
486
-
487
- ### Key Implementation Details
488
-
489
- - **Dual-runtime architecture** — `src/index.ts` is the OpenCode 1 plugin,
490
- `src/v2.ts` the OpenCode 2 plugin (`Plugin.define`), `src/tui.ts` the V1
491
- sidebar, `src/cli.ts` the V2 sidebar; `src/shared.ts` and
492
- `src/sidebar-model.ts` hold the shared pure helpers; `src/worker-client.ts`
493
- is a static HTTP client; `src/tui-registration.ts` self-heals `tui.json`
494
- (V1) and `cli.json` (V2).
495
- - **Static package bridges** — `src/package-server.ts` exposes `{ id, server,
496
- setup }` at both `.` and `./server`; `src/package-tui.ts` exposes `{ id, tui,
497
- setup }` at `./tui`. V1 reads `server`/`tui`; V2 reads `setup`. Neither bridge
498
- invokes both adapters. Both installed server loaders prefer `./server`, so
499
- routing only the root export to V2 is insufficient. Never combine `server`
500
- and `tui` in one object: V1 rejects that shape.
501
- - **Host renderer ownership** — the built TUI bridge uses explicit `.js`
502
- relative imports so the host discovers both adapter modules and routes their
503
- OpenTUI/Solid imports to its own runtime. Extensionless bridge imports can bypass
504
- that prescan and crash the sidebar with `No renderer found`.
505
- - **Zero runtime dependencies for server plugins** — the OpenCode plugin SDK
506
- is bundled into `dist/` (V1 `index.js` ~490 KB, V2 `v2.js` ~160 KB including
507
- bounded-response validation, using a deep SDK import that skips effect). The TUI entrypoints keep
508
- `@opentui/solid` external (provided by the host TUI).
509
- - **Reactive sidebar** — collapse state and view data are solid-js signals
510
- (shared with OpenCode's own solid instance via `--external solid-js`), so
511
- clicking the header re-renders reliably; falls back to plain closures if
512
- solid-js cannot be resolved.
513
- - **No console logging** — `console.*` output corrupts the OpenCode TUI;
514
- the plugin never logs and never throws from hooks.
515
- - **Deferred toast** — health toasts only happen after hook execution begins,
516
- avoiding startup crashes caused by early TUI access.
517
- - **Auto-start** — if the worker is down on load, spawns
518
- `bunx claude-mem start` once per OpenCode process (skipped if `bun` is not
519
- on `PATH`). Health is rechecked on subsequent hooks, so a recovered Worker
520
- does not require restarting OpenCode.
521
- - **Context caching** — base memory context is reused per session until a
522
- compaction boundary invalidates it. Failed fetches are not cached.
523
- - **Circular memory protection** — injected context is wrapped in
524
- `<claude-mem-context>` tags, Claude-Mem search tools are skipped from
525
- observation capture, and memory tags are stripped before storage.
526
- - **Observation hardening** — low-value meta tools skipped; oversized payloads
527
- truncated by UTF-8 byte size (24 KB cap).
528
- - **Field name correctness** — worker payloads use `contentSessionId`, not
529
- `claudeSessionId` (the wrong name fails silently).
530
- - **Session lifecycle hygiene** — `session.deleted` flushes pending observations
531
- and releases local tracking. Claude-Mem self-completes processing; the removed
532
- `/api/sessions/complete` endpoint is not called.
533
- - **Attribution** — observations include the OpenCode call ID as `tool_use_id`;
534
- summaries include the latest assistant's `observedModel` when available.
535
- - **Write status** — failed HTTP writes return failure without throwing; a summary
536
- toast is shown only after the Worker accepts the request, not after generation.
537
-
538
- ## Troubleshooting
539
-
540
- ### No memory appears in OpenCode
541
-
542
- - Confirm the worker is running:
543
-
544
- ```bash
545
- curl -s http://127.0.0.1:37777/api/health
546
- ```
547
-
548
- - Make sure Claude-Mem has already been installed and used from Claude Code.
549
- - Start a fresh OpenCode session after the worker is healthy.
123
+ | `mem-search` / `mem-timeline` / `mem-get-observations` | `claude_mem_search` / `claude_mem_timeline` / `claude_mem_get_observations` (official plugin) and the MCP `search` / `timeline` / `get_observations` |
124
+ | `mem-save` | MCP `save_memory` |
125
+ | Read-file history appended to `read` output | Not available |
126
+ | Opt-in semantic injection | Not available |
550
127
 
551
- ### OpenCode shows `Worker offline`
128
+ Follow steps 2–5 above, then keep the bare name in `cli.json` (and `tui.json` for V1).
552
129
 
553
- The plugin tries to launch the worker via `bunx claude-mem start` once on
554
- plugin load. If the toast still appears:
555
-
556
- - Confirm `bun` is on your `PATH` — `bun --version` should print a version.
557
- - Confirm `claude-mem` is installed for `bunx` — run
558
- `bunx claude-mem --version` once to populate the cache.
559
- - On Windows after a forced kill, port `37777` may stay in `TIME_WAIT` for
560
- 30-120 seconds; wait it out or restart Claude Code.
561
- - Restart Claude Code to bring Claude-Mem back up via its own supervisor.
562
-
563
- ### Sidebar Memory section is missing
564
-
565
- - **OpenCode 1**: check `~/.config/opencode/tui.json` contains this plugin in
566
- its `plugin` array — the plugin self-heals this file on load, so restarting
567
- OpenCode twice (once to heal, once to load) fixes a missing entry.
568
- - **OpenCode 2**: check `~/.config/opencode/cli.json` contains
569
- `@ephemushroom/opencode-claude-mem` in its `plugins` array — self-healed
570
- the same way (comment-bearing cli.json files are left untouched, so remove
571
- the comments or add the entry manually).
572
- - The sidebar requires OpenCode's `@opentui/solid` runtime; if unavailable the
573
- section is skipped silently while hooks and tools keep working.
574
-
575
- ### Clicking the Memory header does nothing
576
-
577
- - Upgrade to ≥ 0.4.2 — earlier versions used non-reactive state and the
578
- toggle never re-rendered.
579
-
580
- ### Duplicate observations / duplicate toasts
581
-
582
- - You are likely running both this plugin and a Claude Code compatibility
583
- bridge for claude-mem. See
584
- [Disable the Claude Code bridge](#using-with-oh-my-openagent-disable-the-claude-code-bridge).
585
-
586
- ### Observations are missing or incomplete
130
+ ## Troubleshooting
587
131
 
588
- - Low-value meta tools and Claude-Mem search tools are skipped by design.
589
- - Very large tool outputs are truncated before storage (24 KB).
132
+ - **`(offline)`**: the Worker is not running. Start Claude Code, or `bunx claude-mem start`.
133
+ - **Sidebar missing**: check `~/.config/opencode/cli.json` lists the package; the sidebar
134
+ needs OpenCode's `@opentui/solid` runtime and is skipped silently without it.
135
+ - **Duplicate observations**: two capture integrations are active. Remove this package from
136
+ `opencode.json` `plugins`, and disable any Claude Code compatibility bridge for claude-mem.
137
+ - **MCP not listed in an open session**: run `/reload`; already-open locations may keep the
138
+ previous MCP set until they reload.
590
139
 
591
140
  ## Development
592
141
 
593
142
  ```bash
594
143
  bun install
595
- bun run build # four adapters + package-server/package-tui bridges + declarations
596
- bun test # build first: export tests import the actual dist artifacts
597
- bun run lint # oxlint
598
- bun run fmt:check # oxfmt
599
- bun src/test-fixtures/v2-runtime.ts # installed V2: packed bare package, local model + Worker
600
- bun src/test-fixtures/v2-runtime.ts v1 # installed V1: same package and behavior assertions
601
- bun src/test-fixtures/tui-runtime.ts # installed V2: real PTY session, render and mouse toggle
602
- bun src/test-fixtures/tui-runtime.ts offline # offline sidebar still renders and toggles
603
- bun --conditions=browser src/test-fixtures/cli-driver.ts render # real rendering + mouse click
604
- ```
605
-
606
- If you edit source code locally, rebuild and restart OpenCode to pick up the
607
- new plugin bundle.
608
-
609
- The runtime QA drivers never write to your real config or memory database and
610
- never use paid models. They pack the current build, serve it through a temporary
611
- local registry, and ask the installed host to resolve the bare npm name in an
612
- isolated home/cache. Registry access is needed for package dependencies. Server
613
- QA drives base/opt-in semantic injection, exactly-once tool capture, all four memory tools,
614
- private-save rejection, file-history enrichment without capture pollution, and
615
- summarization. HTTP regression fixtures also cover repeated/distinct IDs, queued
616
- V2 admission versus consumption, private turns, retries and compaction invalidation.
617
- The V2 TUI probe uses ConPTY and xterm terminal cells to open a real session and
618
- capture collapsed, expanded, and re-collapsed states with actual mouse events.
619
- It checks online/offline operation, unchanged CLI config, and zero model requests.
620
- Set `MEM_TUI_EVIDENCE_DIR` to retain text and truecolor ANSI captures. The separate
621
- OpenTUI driver covers component behavior. V1's interactive host remains outside
622
- this check; its server runtime is verified. Browser PNG fidelity is not claimed.
144
+ bun run build # sidebar adapters + TUI bridge + declarations
145
+ bun test # build first: export tests import dist artifacts
146
+ bun run typecheck && bun run lint && bun run fmt:check
147
+ bun src/test-fixtures/tui-runtime.ts # installed opencode: real PTY, render + mouse toggle
148
+ bun src/test-fixtures/tui-runtime.ts offline # offline state renders and toggles
149
+ bun --conditions=browser src/test-fixtures/cli-driver.ts render # OpenTUI component driver
150
+ ```
151
+
152
+ The PTY check packs the current build, serves it from a temporary local registry, and runs
153
+ the installed `opencode` in an isolated home with a fake Worker. It never touches your config
154
+ or memory database and never calls a model. Set `MEM_TUI_EVIDENCE_DIR` to keep the captured
155
+ collapsed, expanded, detail-dialog and re-collapsed frames.
623
156
 
624
157
  ## License
625
158