@ephemushroom/opencode-claude-mem 0.4.4 → 0.6.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 +313 -48
- package/dist/build-tui-bridge.d.ts +1 -0
- package/dist/cli.d.ts +17 -0
- package/dist/cli.js +743 -0
- package/dist/file-context.d.ts +17 -0
- package/dist/index.js +2677 -2129
- package/dist/memory-options.d.ts +12 -0
- package/dist/memory-session.d.ts +22 -0
- package/dist/memory-turn.d.ts +10 -0
- package/dist/memory-worker.d.ts +21 -0
- package/dist/package-server.d.ts +7 -0
- package/dist/package-server.js +10 -0
- package/dist/package-tui.d.ts +6 -0
- package/dist/package-tui.js +8 -0
- package/dist/shared.d.ts +14 -0
- package/dist/sidebar-model.d.ts +50 -0
- package/dist/tui-registration.d.ts +7 -0
- package/dist/tui.d.ts +0 -36
- package/dist/tui.js +83 -36
- package/dist/v2.d.ts +22 -0
- package/dist/v2.js +5133 -0
- package/dist/worker-client.d.ts +9 -6
- package/package.json +27 -11
package/README.md
CHANGED
|
@@ -3,6 +3,12 @@
|
|
|
3
3
|
Persistent memory for [OpenCode](https://opencode.ai), powered 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.0** adds per-turn memory tracking, `mem-save`, file history and opt-in semantic
|
|
10
|
+
injection, and fixes the OpenCode 2 sidebar's `No renderer found` crash.
|
|
11
|
+
|
|
6
12
|
Share the same Claude-Mem worker, database, and memory across your coding
|
|
7
13
|
agents: memories written by Claude Code are visible to OpenCode, and vice
|
|
8
14
|
versa. Previous observations and summaries are injected into new OpenCode
|
|
@@ -24,7 +30,10 @@ flowchart LR
|
|
|
24
30
|
## Quick Start
|
|
25
31
|
|
|
26
32
|
1. Install and configure Claude-Mem in Claude Code.
|
|
27
|
-
2. Add this plugin to your `opencode.json
|
|
33
|
+
2. Add this plugin to your `opencode.json` — pick the section matching your
|
|
34
|
+
OpenCode version:
|
|
35
|
+
|
|
36
|
+
### OpenCode 1
|
|
28
37
|
|
|
29
38
|
```json
|
|
30
39
|
{
|
|
@@ -32,21 +41,78 @@ flowchart LR
|
|
|
32
41
|
}
|
|
33
42
|
```
|
|
34
43
|
|
|
44
|
+
### OpenCode 2
|
|
45
|
+
|
|
46
|
+
```json
|
|
47
|
+
{
|
|
48
|
+
"plugins": ["@ephemushroom/opencode-claude-mem"]
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
35
52
|
3. Restart OpenCode.
|
|
36
53
|
4. Start a session — memory context is injected automatically, the `mem-*`
|
|
37
54
|
tools become available, and a collapsible **Memory** section appears in the
|
|
38
|
-
sidebar.
|
|
55
|
+
sidebar (both versions).
|
|
39
56
|
|
|
40
57
|
Using [oh-my-openagent](https://github.com/code-yeongyu/oh-my-openagent)?
|
|
41
58
|
See [Disable the Claude Code bridge](#using-with-oh-my-openagent-disable-the-claude-code-bridge)
|
|
42
59
|
to avoid running two claude-mem integrations at once.
|
|
43
60
|
|
|
61
|
+
## OpenCode 1 and 2
|
|
62
|
+
|
|
63
|
+
OpenCode 1 and OpenCode 2 install side by side (`opencode` vs `opencode2`) and
|
|
64
|
+
their plugin APIs are incompatible. This package exposes **one bare package
|
|
65
|
+
name for both runtimes**, with thin bridges that select the existing adapter
|
|
66
|
+
contract. No `/v2` or `/cli` suffix is needed:
|
|
67
|
+
|
|
68
|
+
| | OpenCode 1 | OpenCode 2 |
|
|
69
|
+
|---|---|---|
|
|
70
|
+
| **Binary** | `opencode` | `opencode2` |
|
|
71
|
+
| **Server plugin config** | `opencode.json` → `plugin` | `opencode.json` → `plugins` |
|
|
72
|
+
| **Server plugin entry** | `@ephemushroom/opencode-claude-mem` | `@ephemushroom/opencode-claude-mem` |
|
|
73
|
+
| **Sidebar config** | `~/.config/opencode/tui.json` → `plugin` (self-healed) | `~/.config/opencode/cli.json` → `plugins` (self-healed) |
|
|
74
|
+
| **Sidebar config entry** | `@ephemushroom/opencode-claude-mem` | `@ephemushroom/opencode-claude-mem` |
|
|
75
|
+
| **Sidebar resolution** | package `./tui` export, `tui()` | package `./tui` export, `setup()` |
|
|
76
|
+
|
|
77
|
+
Both adapters talk to the same Claude-Mem worker, so memory written from V1,
|
|
78
|
+
V2, and Claude Code is shared. The V2 entrypoint covers the same behavior —
|
|
79
|
+
context injection, tool observation capture, the `mem-search`/`mem-timeline`/
|
|
80
|
+
`mem-get-observations`/`mem-save` tools, per-user-message registration and session summarize/cleanup via the event
|
|
81
|
+
stream.
|
|
82
|
+
|
|
83
|
+
The bare package is tested with `opencode 1.18.29` and
|
|
84
|
+
`opencode2 0.0.0-beta-19151`; v0.6.0 also passes server and real
|
|
85
|
+
sidebar interaction checks on `0.0.0-beta-19192`. The V2 SDK is pinned because preview APIs can
|
|
86
|
+
change. Unified routing requires **v0.5.0 or later**; the older `0.4.4` package
|
|
87
|
+
does not provide it. Upgrade the package in both server and sidebar configurations.
|
|
88
|
+
Existing `/server`, `/tui`, `/v2`, and `/cli` exports remain supported; replace
|
|
89
|
+
an old config entry rather than adding the bare name beside it.
|
|
90
|
+
|
|
91
|
+
After upgrading, finish active tasks, close the client, and restart the V2
|
|
92
|
+
background service with `opencode2 service restart` before reopening `opencode2`.
|
|
93
|
+
For V1, restart `opencode`. Verify server activation with `opencode2 plugin list`.
|
|
94
|
+
|
|
95
|
+
In V2, installing the server plugin
|
|
96
|
+
(`"plugins": ["@ephemushroom/opencode-claude-mem"]`) auto-appends the same
|
|
97
|
+
bare package to `~/.config/opencode/cli.json` on first load. Restart OpenCode
|
|
98
|
+
after that first load if the sidebar is not yet present. Beta `19151` still
|
|
99
|
+
uses a separate CLI config; this is not a promise of same-launch discovery
|
|
100
|
+
from the server config alone. To register manually, use:
|
|
101
|
+
|
|
102
|
+
```json
|
|
103
|
+
{
|
|
104
|
+
"plugins": ["@ephemushroom/opencode-claude-mem"]
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
44
108
|
## What You Get
|
|
45
109
|
|
|
46
110
|
| Surface | What it looks like |
|
|
47
111
|
|---|---|
|
|
48
112
|
| **System prompt** | `<claude-mem-context>` block with recent observations + session summaries for the current project |
|
|
49
|
-
| **Tools** | `mem-search` → `mem-timeline` → `mem-get-observations
|
|
113
|
+
| **Tools** | `mem-search` → `mem-timeline` → `mem-get-observations`, plus explicit `mem-save` (no MCP server needed) |
|
|
114
|
+
| **Read history** | Bounded supplementary file history after eligible file reads; original file output is preserved |
|
|
115
|
+
| **Optional semantic context** | Relevant project memories for the current real user prompt; **off by default** |
|
|
50
116
|
| **Sidebar** | `▶ Memory (online, 11.7k obs)` — click to expand recent sessions and latest observations |
|
|
51
117
|
| **Background** | Every tool call, assistant message, and file edit captured as observations; sessions summarized on idle |
|
|
52
118
|
|
|
@@ -79,15 +145,21 @@ MCP/Context sections — borderless, click-to-toggle:
|
|
|
79
145
|
loop cheap (stats every 5s).
|
|
80
146
|
- Fails open: worker offline → shows the offline state, never blocks the TUI.
|
|
81
147
|
|
|
82
|
-
The sidebar loads via the package's `./tui`
|
|
83
|
-
|
|
84
|
-
plugin
|
|
85
|
-
|
|
86
|
-
|
|
148
|
+
The sidebar loads via the package's `./tui` bridge in both versions (OpenCode 2
|
|
149
|
+
registers the bare name in `~/.config/opencode/cli.json`). On startup
|
|
150
|
+
the plugin **self-heals the TUI config**: if it is registered as a server
|
|
151
|
+
plugin but missing from the TUI plugin list, it appends itself — no manual
|
|
152
|
+
configuration. OpenCode 1 writes into `tui.json` (symlinked files are written
|
|
153
|
+
through, preserving dotfiles setups); OpenCode 2 appends
|
|
154
|
+
`@ephemushroom/opencode-claude-mem` to `cli.json`'s `plugins` array.
|
|
155
|
+
Existing bare, `/cli`, or `/tui` sidebar entries (including versions and
|
|
156
|
+
object-form options) are left untouched, without adding another entry.
|
|
157
|
+
Malformed or comment-bearing CLI config is left untouched; add the bare name
|
|
158
|
+
manually in that case.
|
|
87
159
|
|
|
88
160
|
### Memory Tools
|
|
89
161
|
|
|
90
|
-
Three native OpenCode tools cover the same 3-step workflow as the upstream
|
|
162
|
+
Three native OpenCode tools cover the same 3-step search workflow as the upstream
|
|
91
163
|
Claude-Mem MCP server — no MCP server or stdio subprocess required:
|
|
92
164
|
|
|
93
165
|
```mermaid
|
|
@@ -101,6 +173,118 @@ flowchart LR
|
|
|
101
173
|
| `mem-search` | `GET /api/search` | Formatted index with query, project, source, type, date, pagination, and ordering filters |
|
|
102
174
|
| `mem-timeline` | `GET /api/timeline` | Chronological records around an `anchor` ID (or auto-located via `query`) |
|
|
103
175
|
| `mem-get-observations` | `POST /api/observations/batch` | Full details for IDs — e.g. the IDs shown in the injected context |
|
|
176
|
+
| `mem-save` | `POST /api/memory/save` | Explicitly save required `text`, optional `title` and `project` (defaults to current project) |
|
|
177
|
+
|
|
178
|
+
`mem-save` strips `<private>` and `<claude-mem-context>` material from text/title,
|
|
179
|
+
rejects empty/fully stripped content and values above 24 KiB, and reports success
|
|
180
|
+
only for a valid Worker acknowledgement. It never retries automatically: after
|
|
181
|
+
a timeout, check with search before retrying because the write may have succeeded.
|
|
182
|
+
Manual saves do not require an observation-model inference pass, but the Worker
|
|
183
|
+
still performs asynchronous vector indexing; this is **not** a promise of zero
|
|
184
|
+
embedding cost. Native and namespaced `mem-save` calls are excluded from automatic
|
|
185
|
+
observation capture.
|
|
186
|
+
|
|
187
|
+
### Memory configuration (both runtimes)
|
|
188
|
+
|
|
189
|
+
The per-turn registration, `mem-save`, file history, and semantic options described
|
|
190
|
+
here require **v0.6.0 or later**. Upgrade both server and sidebar package entries;
|
|
191
|
+
`0.5.0` does not include these features or the renderer fix.
|
|
192
|
+
|
|
193
|
+
Configuration is read once when the server plugin is set up. Quit and restart
|
|
194
|
+
OpenCode after changing options, environment, or settings; on V2 also restart the
|
|
195
|
+
background service. No semantic feature is enabled globally by this plugin.
|
|
196
|
+
|
|
197
|
+
**OpenCode 1** uses a tuple in `opencode.json`:
|
|
198
|
+
|
|
199
|
+
```json
|
|
200
|
+
{
|
|
201
|
+
"plugin": [["@ephemushroom/opencode-claude-mem", {
|
|
202
|
+
"semanticInjection": { "enabled": true, "limit": 5, "maxChars": 6000, "timeoutMs": 2000 },
|
|
203
|
+
"fileContext": { "enabled": true, "limit": 15, "maxChars": 6000, "timeoutMs": 1500 }
|
|
204
|
+
}]]
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**OpenCode 2** uses a package/options object (not a V1 tuple):
|
|
209
|
+
|
|
210
|
+
```json
|
|
211
|
+
{
|
|
212
|
+
"plugins": [{
|
|
213
|
+
"package": "@ephemushroom/opencode-claude-mem",
|
|
214
|
+
"options": {
|
|
215
|
+
"semanticInjection": { "enabled": true, "limit": 5, "maxChars": 6000, "timeoutMs": 2000 },
|
|
216
|
+
"fileContext": { "enabled": true, "limit": 15, "maxChars": 6000, "timeoutMs": 1500 }
|
|
217
|
+
}
|
|
218
|
+
}]
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
| Option | Semantic default | File-history default | Accepted values |
|
|
223
|
+
|---|---|---|---|
|
|
224
|
+
| `enabled` | `false` | `true` | Boolean |
|
|
225
|
+
| `limit` | `5` | `15` | Integer 1–50 |
|
|
226
|
+
| `maxChars` | `6000` | `6000` | Integer 256–24000; supplementary payload only |
|
|
227
|
+
| `timeoutMs` | `2000` | `1500` | Integer 100–10000 |
|
|
228
|
+
|
|
229
|
+
Per-field precedence for semantic `enabled` and `limit` is **explicit plugin
|
|
230
|
+
options > environment > `~/.claude-mem/settings.json` > defaults**. The environment
|
|
231
|
+
and settings keys are `CLAUDE_MEM_SEMANTIC_INJECT` (`true`/`false` or `1`/`0`) and
|
|
232
|
+
`CLAUDE_MEM_SEMANTIC_INJECT_LIMIT` (integer 1–50). Settings may use booleans/numbers
|
|
233
|
+
or their string equivalents. Explicit `false` overrides environment `true`.
|
|
234
|
+
Invalid values fall through to the next valid source; other controls use explicit
|
|
235
|
+
options then defaults. Settings are read-only and never logged.
|
|
236
|
+
|
|
237
|
+
Semantic retrieval uses `POST /api/context/semantic` with `{q, project, limit}`
|
|
238
|
+
and reads JSON `{context, count}`. `q` is the sanitized real current prompt,
|
|
239
|
+
at least 20 characters; media-only, fully private, and identifiable internal
|
|
240
|
+
messages are excluded. No `platformSource` filter is sent, so Claude Code and
|
|
241
|
+
OpenCode memories remain shared. Each real prompt identity gets at most one
|
|
242
|
+
retrieval attempt, including failure/timeout, reused across tool-loop dispatches.
|
|
243
|
+
The system block is replaced rather than accumulated. Old Workers, malformed
|
|
244
|
+
responses and offline retrieval fail open.
|
|
245
|
+
|
|
246
|
+
File history uses `GET /api/observations/by-file` with repeated `path` parameters
|
|
247
|
+
(absolute and project-relative, normalized to forward slashes), `projects` and `limit`. Supported read arguments
|
|
248
|
+
include V1 `filePath`, V2 `path`, and `file_path`/`filePaths`/`file_paths` aliases.
|
|
249
|
+
Only existing regular files of at least 1500 bytes qualify, at most 10 paths per
|
|
250
|
+
read. Lookups run concurrently within one configured per-read deadline (including
|
|
251
|
+
file metadata lookup), not ten serial timeout periods. Rows are deduplicated by
|
|
252
|
+
memory session and the displayed text is bounded; a file whose mtime is at least
|
|
253
|
+
the newest observation timestamp gets no history. Delivered history is deduplicated per
|
|
254
|
+
session/file revision (mtime and size); failures and timeouts may retry on a later read.
|
|
255
|
+
Editing the file or starting a new session also permits another lookup. Supplementary history is explicitly
|
|
256
|
+
labeled and wrapped in memory tags, appended without replacing/truncating the
|
|
257
|
+
original read result, and never included in the automatic tool observation.
|
|
258
|
+
Known child sessions (`parentID` when supplied by the host) skip file-history
|
|
259
|
+
enrichment; V1 resumed sessions without that metadata cannot reliably be identified
|
|
260
|
+
as subagents and are not guessed from the agent's name.
|
|
261
|
+
|
|
262
|
+
### Prompt lifecycle and Worker limitations
|
|
263
|
+
|
|
264
|
+
V1 registers stable `chat.message` IDs; V2 records admission IDs without writing,
|
|
265
|
+
then registers only IDs present in delivered `session.context` records at model
|
|
266
|
+
dispatch. Thus queued V2 prompts do not advance the Worker's current prompt.
|
|
267
|
+
Resumed sessions without local admission history use the latest delivered real
|
|
268
|
+
user turn, not a synthetic `SESSION_START`, and tool callbacks do not reread the
|
|
269
|
+
whole history. Registrations are serialized per session and successful IDs are
|
|
270
|
+
deduplicated locally; a failed registration retains its real prompt for retry.
|
|
271
|
+
Media-only turns use `[media prompt]`. Fully private or identifiable synthetic
|
|
272
|
+
turns suppress automatic observations, summaries and semantic retrieval locally,
|
|
273
|
+
including when the Worker reports a skipped/private registration.
|
|
274
|
+
|
|
275
|
+
**Upstream v13.24.1 still deduplicates identical prompt text within its own time
|
|
276
|
+
window and has no consumed external prompt-ID contract.** This plugin submits
|
|
277
|
+
distinct IDs even if their text is identical, without prefixing/changing the text,
|
|
278
|
+
but cannot guarantee distinct database prompt rows. A `duplicate` acknowledgement
|
|
279
|
+
is accepted; other skipped registrations do not authorize automatic capture.
|
|
280
|
+
V1 exposes submission callbacks rather than V2's delivered/inbox distinction;
|
|
281
|
+
it cannot provide the same queued-consumption guarantee. A restart cannot restore
|
|
282
|
+
the plugin's in-memory ID dedupe ledger, so upstream dedupe remains relevant.
|
|
283
|
+
|
|
284
|
+
Base context is session-isolated, limited to 24000 characters, with a two-second
|
|
285
|
+
request timeout. It is refreshed at V1 compaction start/end and V2 compaction
|
|
286
|
+
requests/completed events or changed compaction records. Failed reads are retryable;
|
|
287
|
+
an invalidated or deleted session cannot be repopulated by an older in-flight read.
|
|
104
288
|
|
|
105
289
|
## How It Works
|
|
106
290
|
|
|
@@ -111,27 +295,46 @@ sequenceDiagram
|
|
|
111
295
|
participant W as Claude-Mem Worker
|
|
112
296
|
|
|
113
297
|
OC->>P: plugin loads
|
|
114
|
-
P->>P: self-heal tui.json
|
|
298
|
+
P->>P: self-heal tui.json / cli.json
|
|
115
299
|
P->>W: health check (auto-start via bunx if down)
|
|
116
|
-
OC->>P: chat.message
|
|
300
|
+
OC->>P: each real user prompt (chat.message / delivered context)
|
|
117
301
|
P->>W: POST /api/sessions/init
|
|
118
|
-
OC->>P: system
|
|
302
|
+
OC->>P: system prompt transform / context hook
|
|
119
303
|
W-->>P: GET /api/context/inject (cached per session)
|
|
120
304
|
P-->>OC: inject <claude-mem-context> into system prompt
|
|
121
305
|
loop during the session
|
|
122
|
-
OC->>P: tool.execute.after /
|
|
306
|
+
OC->>P: tool.execute.after / assistant text / file.edited
|
|
123
307
|
P->>W: POST /api/sessions/observations
|
|
124
308
|
end
|
|
125
|
-
OC->>P: session
|
|
309
|
+
OC->>P: session idle / execution succeeded / compaction ended
|
|
126
310
|
P->>W: POST /api/sessions/summarize
|
|
127
311
|
OC->>P: session.deleted
|
|
128
|
-
P->>
|
|
312
|
+
P->>P: flush pending observations and release local state
|
|
129
313
|
```
|
|
130
314
|
|
|
131
315
|
The plugin is intentionally small: it only adapts OpenCode hook events to the
|
|
132
316
|
Claude-Mem worker HTTP API. All indexing, summarization, memory search, and
|
|
133
317
|
storage stay in upstream Claude-Mem.
|
|
134
318
|
|
|
319
|
+
### Hook Mapping (V1 ↔ V2)
|
|
320
|
+
|
|
321
|
+
The V1 and V2 entrypoints adapt the same behavior onto two different plugin
|
|
322
|
+
APIs:
|
|
323
|
+
|
|
324
|
+
| Behavior | OpenCode 1 | OpenCode 2 |
|
|
325
|
+
|---|---|---|
|
|
326
|
+
| Inject memory context | `experimental.chat.system.transform` | `ctx.session.hook('context')` |
|
|
327
|
+
| Refresh memory on compaction | `experimental.session.compacting` + `session.compacted` | compaction model requests/events + delivered compaction records |
|
|
328
|
+
| Register each real user prompt | `chat.message` (message ID) | `ctx.session.hook('context')` (`ctx.session.context` delivered IDs; not inbox admission) |
|
|
329
|
+
| Capture tool observations | `tool.execute.after` | `ctx.tool.hook('execute.after')` |
|
|
330
|
+
| Custom memory tools | `tool` (`mem-search`, …) | `ctx.tool.transform` (`tools.add`) |
|
|
331
|
+
| Assistant text capture | `event` (`message.updated`, debounced 250ms) | `session.text.ended` event (complete text) |
|
|
332
|
+
| File edit observations | `event` (`file.edited`) | `ctx.tool.hook('execute.after')` |
|
|
333
|
+
| Summarize on idle | `event` (`session.idle`) | `session.execution.succeeded` event |
|
|
334
|
+
| Summarize after compaction | `event` (`session.compacted`) | `session.compaction.ended` event |
|
|
335
|
+
| Release session tracking | `event` (`session.deleted`) | `session.deleted` event |
|
|
336
|
+
| Sidebar slot | `api.slots.register` (`sidebar_content`) | `ctx.ui.slot({ append: 'sidebar.content', render })` |
|
|
337
|
+
|
|
135
338
|
### Cross-Tool Memory Sharing
|
|
136
339
|
|
|
137
340
|
Writes are attributed (`platformSource: "opencode"`), reads are shared:
|
|
@@ -169,12 +372,25 @@ Restart Claude Code so the worker can start and initialize its data directory.
|
|
|
169
372
|
|
|
170
373
|
Add this plugin to your project or global `opencode.json`:
|
|
171
374
|
|
|
375
|
+
**OpenCode 1** (`opencode`):
|
|
376
|
+
|
|
172
377
|
```json
|
|
173
378
|
{
|
|
174
379
|
"plugin": ["@ephemushroom/opencode-claude-mem"]
|
|
175
380
|
}
|
|
176
381
|
```
|
|
177
382
|
|
|
383
|
+
**OpenCode 2** (`opencode2`):
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{
|
|
387
|
+
"plugins": ["@ephemushroom/opencode-claude-mem"]
|
|
388
|
+
}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
The two runtimes can be installed side by side and use the same package name.
|
|
392
|
+
Their config keys remain different: `plugin` in V1, `plugins` in V2.
|
|
393
|
+
|
|
178
394
|
Then restart OpenCode.
|
|
179
395
|
|
|
180
396
|
### Step 3: Verify
|
|
@@ -183,9 +399,9 @@ Then restart OpenCode.
|
|
|
183
399
|
curl -s http://127.0.0.1:37777/api/health
|
|
184
400
|
```
|
|
185
401
|
|
|
186
|
-
If the worker is healthy,
|
|
187
|
-
`Memory active · <project>` when a session
|
|
188
|
-
|
|
402
|
+
If the worker is healthy, the sidebar shows `▶ Memory (online, … obs)`.
|
|
403
|
+
OpenCode 1 also shows a `Memory active · <project>` toast when a session
|
|
404
|
+
starts (OpenCode 2 only toasts a warning when the worker is offline).
|
|
189
405
|
|
|
190
406
|
## Using with oh-my-openagent: Disable the Claude Code Bridge
|
|
191
407
|
|
|
@@ -227,20 +443,23 @@ worker. Everything the bridge provided is covered natively by this plugin:
|
|
|
227
443
|
|
|
228
444
|
### Hook Mapping
|
|
229
445
|
|
|
230
|
-
|
|
446
|
+
Claude Code hooks → this plugin's adapters (see [Hook Mapping (V1 ↔ V2)](#hook-mapping-v1--v2)
|
|
447
|
+
for the per-runtime API):
|
|
448
|
+
|
|
449
|
+
| Claude Code | OpenCode (both runtimes) | Purpose |
|
|
231
450
|
|---|---|---|
|
|
232
|
-
| `SessionStart` | `
|
|
233
|
-
| `SessionStart` | `
|
|
234
|
-
| `UserPromptSubmit` | `chat.message` | Initialize session with real user prompt |
|
|
235
|
-
| `PostToolUse` | `tool.execute.after` | Capture tool observations |
|
|
236
|
-
| Claude-Mem MCP `search` | `
|
|
237
|
-
| Claude-Mem MCP `timeline` | `
|
|
238
|
-
| Claude-Mem MCP `get_observations` | `
|
|
239
|
-
| _(streaming)_ |
|
|
240
|
-
| _(streaming)_ |
|
|
241
|
-
| _(compaction)_ | `
|
|
242
|
-
| `Stop` | `
|
|
243
|
-
| `SessionEnd` | `
|
|
451
|
+
| `SessionStart` | system prompt injection (`system.transform` / `context` hook) | Inject memory context |
|
|
452
|
+
| `SessionStart` | compaction injection (`session.compacting` / context hook) | Preserve memory during compaction |
|
|
453
|
+
| `UserPromptSubmit` | session init (`chat.message` / context hook) | Initialize session with real user prompt |
|
|
454
|
+
| `PostToolUse` | observation capture (`tool.execute.after` / `execute.after` hook) | Capture tool observations |
|
|
455
|
+
| Claude-Mem MCP `search` | `mem-search` tool | Search memory from OpenCode |
|
|
456
|
+
| Claude-Mem MCP `timeline` | `mem-timeline` tool | Chronological context around an observation |
|
|
457
|
+
| Claude-Mem MCP `get_observations` | `mem-get-observations` tool | Fetch full observation details by ID |
|
|
458
|
+
| _(streaming)_ | assistant text (`message.updated` / `session.text.ended`) | Capture assistant text |
|
|
459
|
+
| _(streaming)_ | file edits (`file.edited` / tool hook) | Record file edit observations |
|
|
460
|
+
| _(compaction)_ | `session.compacted` / `session.compaction.ended` | Summarize after OpenCode compacts |
|
|
461
|
+
| `Stop` | `session.idle` / `session.execution.succeeded` | Flush + summarize |
|
|
462
|
+
| `SessionEnd` | `session.deleted` | Flush observations and release local state; Worker self-completes |
|
|
244
463
|
|
|
245
464
|
### Worker API Endpoints Used
|
|
246
465
|
|
|
@@ -251,10 +470,12 @@ worker. Everything the bridge provided is covered natively by this plugin:
|
|
|
251
470
|
| `POST` | `/api/sessions/init` | Initialize session |
|
|
252
471
|
| `POST` | `/api/sessions/observations` | Store tool observation |
|
|
253
472
|
| `POST` | `/api/sessions/summarize` | Trigger summarization |
|
|
254
|
-
| `POST` | `/api/sessions/complete` | Complete session |
|
|
255
473
|
| `GET` | `/api/search?query=...&project=...&dateStart=...&dateEnd=...` | `mem-search`; also supports `limit`, `platformSource`, `type`, `obs_type`, `offset`, and `orderBy` |
|
|
256
474
|
| `GET` | `/api/timeline?project={name}&anchor={id}` | `mem-timeline` |
|
|
257
475
|
| `POST` | `/api/observations/batch` | `mem-get-observations` |
|
|
476
|
+
| `POST` | `/api/memory/save` | Explicit `mem-save`, nested `metadata.platformSource` attribution |
|
|
477
|
+
| `POST` | `/api/context/semantic` | Opt-in semantic context; JSON `{context,count}` response |
|
|
478
|
+
| `GET` | `/api/observations/by-file?path=...&path=...&projects=...&limit=...` | Supplementary file history |
|
|
258
479
|
| `GET` | `/api/stats` + `/api/processing-status` | Sidebar status |
|
|
259
480
|
| `GET` | `/api/summaries` + `/api/observations` | Sidebar recent items (expanded only) |
|
|
260
481
|
|
|
@@ -264,11 +485,26 @@ The worker endpoint is resolved in this order: `CLAUDE_MEM_WORKER_HOST` /
|
|
|
264
485
|
|
|
265
486
|
### Key Implementation Details
|
|
266
487
|
|
|
267
|
-
- **
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
-
|
|
271
|
-
|
|
488
|
+
- **Dual-runtime architecture** — `src/index.ts` is the OpenCode 1 plugin,
|
|
489
|
+
`src/v2.ts` the OpenCode 2 plugin (`Plugin.define`), `src/tui.ts` the V1
|
|
490
|
+
sidebar, `src/cli.ts` the V2 sidebar; `src/shared.ts` and
|
|
491
|
+
`src/sidebar-model.ts` hold the shared pure helpers; `src/worker-client.ts`
|
|
492
|
+
is a static HTTP client; `src/tui-registration.ts` self-heals `tui.json`
|
|
493
|
+
(V1) and `cli.json` (V2).
|
|
494
|
+
- **Static package bridges** — `src/package-server.ts` exposes `{ id, server,
|
|
495
|
+
setup }` at both `.` and `./server`; `src/package-tui.ts` exposes `{ id, tui,
|
|
496
|
+
setup }` at `./tui`. V1 reads `server`/`tui`; V2 reads `setup`. Neither bridge
|
|
497
|
+
invokes both adapters. Both installed server loaders prefer `./server`, so
|
|
498
|
+
routing only the root export to V2 is insufficient. Never combine `server`
|
|
499
|
+
and `tui` in one object: V1 rejects that shape.
|
|
500
|
+
- **Host renderer ownership** — the built TUI bridge uses explicit `.js`
|
|
501
|
+
relative imports so the host discovers both adapter modules and routes their
|
|
502
|
+
OpenTUI/Solid imports to its own runtime. Extensionless bridge imports can bypass
|
|
503
|
+
that prescan and crash the sidebar with `No renderer found`.
|
|
504
|
+
- **Zero runtime dependencies for server plugins** — the OpenCode plugin SDK
|
|
505
|
+
is bundled into `dist/` (V1 `index.js` ~490 KB, V2 `v2.js` ~160 KB including
|
|
506
|
+
bounded-response validation, using a deep SDK import that skips effect). The TUI entrypoints keep
|
|
507
|
+
`@opentui/solid` external (provided by the host TUI).
|
|
272
508
|
- **Reactive sidebar** — collapse state and view data are solid-js signals
|
|
273
509
|
(shared with OpenCode's own solid instance via `--external solid-js`), so
|
|
274
510
|
clicking the header re-renders reliably; falls back to plain closures if
|
|
@@ -279,9 +515,10 @@ The worker endpoint is resolved in this order: `CLAUDE_MEM_WORKER_HOST` /
|
|
|
279
515
|
avoiding startup crashes caused by early TUI access.
|
|
280
516
|
- **Auto-start** — if the worker is down on load, spawns
|
|
281
517
|
`bunx claude-mem start` once per OpenCode process (skipped if `bun` is not
|
|
282
|
-
on `PATH`).
|
|
283
|
-
|
|
284
|
-
|
|
518
|
+
on `PATH`). Health is rechecked on subsequent hooks, so a recovered Worker
|
|
519
|
+
does not require restarting OpenCode.
|
|
520
|
+
- **Context caching** — base memory context is reused per session until a
|
|
521
|
+
compaction boundary invalidates it. Failed fetches are not cached.
|
|
285
522
|
- **Circular memory protection** — injected context is wrapped in
|
|
286
523
|
`<claude-mem-context>` tags, Claude-Mem search tools are skipped from
|
|
287
524
|
observation capture, and memory tags are stripped before storage.
|
|
@@ -289,9 +526,13 @@ The worker endpoint is resolved in this order: `CLAUDE_MEM_WORKER_HOST` /
|
|
|
289
526
|
truncated by UTF-8 byte size (24 KB cap).
|
|
290
527
|
- **Field name correctness** — worker payloads use `contentSessionId`, not
|
|
291
528
|
`claudeSessionId` (the wrong name fails silently).
|
|
292
|
-
- **Session lifecycle hygiene** — `session.deleted`
|
|
293
|
-
|
|
294
|
-
`
|
|
529
|
+
- **Session lifecycle hygiene** — `session.deleted` flushes pending observations
|
|
530
|
+
and releases local tracking. Claude-Mem self-completes processing; the removed
|
|
531
|
+
`/api/sessions/complete` endpoint is not called.
|
|
532
|
+
- **Attribution** — observations include the OpenCode call ID as `tool_use_id`;
|
|
533
|
+
summaries include the latest assistant's `observedModel` when available.
|
|
534
|
+
- **Write status** — failed HTTP writes return failure without throwing; a summary
|
|
535
|
+
toast is shown only after the Worker accepts the request, not after generation.
|
|
295
536
|
|
|
296
537
|
## Troubleshooting
|
|
297
538
|
|
|
@@ -320,9 +561,13 @@ plugin load. If the toast still appears:
|
|
|
320
561
|
|
|
321
562
|
### Sidebar Memory section is missing
|
|
322
563
|
|
|
323
|
-
-
|
|
324
|
-
array — the plugin self-heals this file on load, so restarting
|
|
325
|
-
twice (once to heal, once to load) fixes a missing entry.
|
|
564
|
+
- **OpenCode 1**: check `~/.config/opencode/tui.json` contains this plugin in
|
|
565
|
+
its `plugin` array — the plugin self-heals this file on load, so restarting
|
|
566
|
+
OpenCode twice (once to heal, once to load) fixes a missing entry.
|
|
567
|
+
- **OpenCode 2**: check `~/.config/opencode/cli.json` contains
|
|
568
|
+
`@ephemushroom/opencode-claude-mem` in its `plugins` array — self-healed
|
|
569
|
+
the same way (comment-bearing cli.json files are left untouched, so remove
|
|
570
|
+
the comments or add the entry manually).
|
|
326
571
|
- The sidebar requires OpenCode's `@opentui/solid` runtime; if unavailable the
|
|
327
572
|
section is skipped silently while hooks and tools keep working.
|
|
328
573
|
|
|
@@ -346,15 +591,35 @@ plugin load. If the toast still appears:
|
|
|
346
591
|
|
|
347
592
|
```bash
|
|
348
593
|
bun install
|
|
349
|
-
bun run build #
|
|
350
|
-
bun test #
|
|
594
|
+
bun run build # four adapters + package-server/package-tui bridges + declarations
|
|
595
|
+
bun test # build first: export tests import the actual dist artifacts
|
|
351
596
|
bun run lint # oxlint
|
|
352
597
|
bun run fmt:check # oxfmt
|
|
598
|
+
bun src/test-fixtures/v2-runtime.ts # installed V2: packed bare package, local model + Worker
|
|
599
|
+
bun src/test-fixtures/v2-runtime.ts v1 # installed V1: same package and behavior assertions
|
|
600
|
+
bun src/test-fixtures/tui-runtime.ts # installed V2: real PTY session, render and mouse toggle
|
|
601
|
+
bun src/test-fixtures/tui-runtime.ts offline # offline sidebar still renders and toggles
|
|
602
|
+
bun --conditions=browser src/test-fixtures/cli-driver.ts render # real rendering + mouse click
|
|
353
603
|
```
|
|
354
604
|
|
|
355
605
|
If you edit source code locally, rebuild and restart OpenCode to pick up the
|
|
356
606
|
new plugin bundle.
|
|
357
607
|
|
|
608
|
+
The runtime QA drivers never write to your real config or memory database and
|
|
609
|
+
never use paid models. They pack the current build, serve it through a temporary
|
|
610
|
+
local registry, and ask the installed host to resolve the bare npm name in an
|
|
611
|
+
isolated home/cache. Registry access is needed for package dependencies. Server
|
|
612
|
+
QA drives base/opt-in semantic injection, exactly-once tool capture, all four memory tools,
|
|
613
|
+
private-save rejection, file-history enrichment without capture pollution, and
|
|
614
|
+
summarization. HTTP regression fixtures also cover repeated/distinct IDs, queued
|
|
615
|
+
V2 admission versus consumption, private turns, retries and compaction invalidation.
|
|
616
|
+
The V2 TUI probe uses ConPTY and xterm terminal cells to open a real session and
|
|
617
|
+
capture collapsed, expanded, and re-collapsed states with actual mouse events.
|
|
618
|
+
It checks online/offline operation, unchanged CLI config, and zero model requests.
|
|
619
|
+
Set `MEM_TUI_EVIDENCE_DIR` to retain text and truecolor ANSI captures. The separate
|
|
620
|
+
OpenTUI driver covers component behavior. V1's interactive host remains outside
|
|
621
|
+
this check; its server runtime is verified. Browser PNG fidelity is not claimed.
|
|
622
|
+
|
|
358
623
|
## License
|
|
359
624
|
|
|
360
625
|
MIT
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode V2 TUI plugin for Claude-Mem (the Memory sidebar).
|
|
3
|
+
*
|
|
4
|
+
* Loaded from `~/.config/opencode/cli.json` via the package's `./cli`
|
|
5
|
+
* export:
|
|
6
|
+
*
|
|
7
|
+
* ```json
|
|
8
|
+
* { "plugins": ["@ephemushroom/opencode-claude-mem/cli"] }
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* V2 TUI plugins are `Plugin.define({ id, setup })` modules rendered inside
|
|
12
|
+
* the TUI process with @opentui/solid elements — the same element model as the
|
|
13
|
+
* V1 sidebar, but registered through a `sidebar.content` slot claim with
|
|
14
|
+
* reactive state from `ctx.storage.memory` (survives hot reloads).
|
|
15
|
+
*/
|
|
16
|
+
declare const _default: import("@opencode-ai/plugin-v2/tui/plugin").Definition;
|
|
17
|
+
export default _default;
|