@ephemushroom/opencode-claude-mem 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bryan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,260 @@
1
+ # opencode-claude-mem
2
+
3
+ OpenCode plugin for [Claude-Mem](https://github.com/thedotmack/claude-mem) persistent memory system.
4
+
5
+ Enables OpenCode to share the same memory database as Claude Code — observations, summaries, and context injection all work across both editors.
6
+
7
+ ## How It Works
8
+
9
+ This plugin communicates with the Claude-Mem worker service (HTTP API on port 37777) to:
10
+
11
+ - **Capture tool observations** — every tool execution is recorded as a structured observation
12
+ - **Inject memory context** — previous session context is injected into the system prompt via `/api/context/inject`
13
+ - **Summarize sessions** — when a session goes idle, the last user/assistant messages are sent for summarization
14
+ - **Search memory** — `mem-search` tool lets the LLM query project history
15
+
16
+ ## Prerequisites
17
+
18
+ - [Claude Code](https://claude.com/claude-code) with [claude-mem plugin](https://github.com/thedotmack/claude-mem) installed and running
19
+ - [OpenCode](https://opencode.ai) with plugin support
20
+
21
+ ## Installation
22
+
23
+ ### Step 1: Install Claude-Mem (if not already)
24
+
25
+ In Claude Code terminal:
26
+
27
+ ```
28
+ /plugin marketplace add thedotmack/claude-mem
29
+ /plugin install claude-mem
30
+ ```
31
+
32
+ Restart Claude Code. The worker service will start automatically on port 37777.
33
+
34
+ ### Step 2: Add the Plugin
35
+
36
+ Add to your `opencode.json` (project or global `~/.config/opencode/opencode.json`):
37
+
38
+ ```json
39
+ {
40
+ "plugin": ["@ephemushroom/opencode-claude-mem"]
41
+ }
42
+ ```
43
+
44
+ Restart OpenCode. The plugin will be installed automatically.
45
+
46
+ ### Step 3: Verify
47
+
48
+ ```bash
49
+ # Check worker is running
50
+ curl -s http://127.0.0.1:37777/api/health
51
+
52
+ # Restart OpenCode — you should see toast: "Memory active · {project}"
53
+ ```
54
+
55
+ ### Alternative: Manual Installation (from source)
56
+
57
+ If you prefer to install from source instead of npm:
58
+
59
+ ```bash
60
+ git clone https://github.com/Ephemushroom/opencode-claude-mem.git
61
+ cd opencode-claude-mem
62
+ bun install
63
+ bun run build
64
+ ```
65
+
66
+ Then symlink into OpenCode:
67
+
68
+ **macOS / Linux:**
69
+
70
+ ```bash
71
+ mkdir -p ~/.config/opencode/plugin
72
+ ln -sf "$(pwd)/dist/index.js" ~/.config/opencode/plugin/claude-mem.js
73
+ ```
74
+
75
+ **Windows (requires elevated terminal):**
76
+
77
+ ```powershell
78
+ # Option A: If you have gsudo / sudo installed
79
+ sudo powershell -Command "New-Item -ItemType SymbolicLink -Path '$env:USERPROFILE\.config\opencode\plugin\claude-mem.js' -Target 'D:\path\to\opencode-claude-mem\dist\index.js'"
80
+
81
+ # Option B: Run PowerShell as Administrator
82
+ New-Item -ItemType SymbolicLink -Path "$env:USERPROFILE\.config\opencode\plugin\claude-mem.js" -Target "D:\path\to\opencode-claude-mem\dist\index.js"
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Windows-Specific Setup
88
+
89
+ ### Python Version for ChromaDB
90
+
91
+ Claude-Mem uses ChromaDB for vector semantic search. ChromaDB's `chroma-mcp` tool requires **Python ≤ 3.13** (Pydantic V1 is incompatible with Python 3.14+).
92
+
93
+ If `uv` defaults to Python 3.14, you'll see:
94
+
95
+ ```
96
+ Chroma connection failed: Chroma server not reachable.
97
+ ```
98
+
99
+ **Fix — set global Python version:**
100
+
101
+ ```bash
102
+ uv python pin --global 3.13
103
+ ```
104
+
105
+ This creates a global `.python-version` file in `%APPDATA%\uv\` so all `uvx` calls default to 3.13.
106
+
107
+ ### ChromaDB on Windows x64
108
+
109
+ The npm `chromadb` package only supports Windows ARM64, not x64. The worker will fail to start Chroma natively.
110
+
111
+ **Fix — use Python ChromaDB instead:**
112
+
113
+ ```bash
114
+ # Install Python ChromaDB CLI
115
+ uv tool install chromadb --python 3.13
116
+ ```
117
+
118
+ Update `~/.claude-mem/settings.json`:
119
+
120
+ ```json
121
+ {
122
+ "CLAUDE_MEM_CHROMA_MODE": "remote"
123
+ }
124
+ ```
125
+
126
+ Start Chroma server manually:
127
+
128
+ ```bash
129
+ chroma run --path %USERPROFILE%\.claude-mem\vector-db --host 127.0.0.1 --port 8000
130
+ ```
131
+
132
+ > **Note**: Chroma server must be running before Claude Code starts. Consider creating a startup script or scheduled task.
133
+
134
+ ### ChromaDB Embedding Model Crash (Bun Runtime)
135
+
136
+ After connecting to Chroma, the worker may crash with:
137
+
138
+ ```
139
+ Collection setup failed: undefined is not an object
140
+ (evaluating 'e.cacheDir = ...')
141
+ ```
142
+
143
+ This happens because `@huggingface/transformers` returns `undefined` when imported through Bun's CJS shim.
144
+
145
+ **Fix — patch `worker-service.cjs`:**
146
+
147
+ Locate the bundled worker file:
148
+
149
+ ```
150
+ ~/.claude/plugins/cache/thedotmack/claude-mem/<version>/scripts/worker-service.cjs
151
+ ```
152
+
153
+ Find this pattern (minified):
154
+
155
+ ```javascript
156
+ let{env:e}=await Promise.resolve().then(()=>(F9(),N9));
157
+ ```
158
+
159
+ Replace with:
160
+
161
+ ```javascript
162
+ let{env:e}=await import("@huggingface/transformers");
163
+ ```
164
+
165
+ Then install the dependency:
166
+
167
+ ```bash
168
+ cd ~/.claude/plugins/cache/thedotmack/claude-mem/<version>
169
+ bun install
170
+ ```
171
+
172
+ > ⚠️ **This patch is lost on plugin updates.** You'll need to re-apply after each claude-mem version upgrade.
173
+
174
+ ### Verifying ChromaDB Works
175
+
176
+ ```bash
177
+ # Check Chroma server
178
+ curl -s http://127.0.0.1:8000/api/v1/heartbeat
179
+
180
+ # Check worker health
181
+ curl -s http://127.0.0.1:37777/api/health
182
+
183
+ # Check for sync errors in worker logs
184
+ # Open http://localhost:37777 in browser for the web viewer
185
+ ```
186
+
187
+ When working correctly, worker logs should show:
188
+
189
+ ```
190
+ CHROMA_SYNC connected (remote mode, 127.0.0.1:8000)
191
+ ```
192
+
193
+ > **ChromaDB is optional.** Without it, claude-mem falls back to SQLite FTS5 full-text search. You lose semantic/vector search but keyword search still works.
194
+
195
+ ---
196
+
197
+ ## Development
198
+
199
+ After editing source:
200
+
201
+ ```bash
202
+ bun run build
203
+ # Restart OpenCode to pick up changes
204
+ ```
205
+
206
+ If using symlink install, changes are picked up after rebuild. If using npm install, bump version and republish.
207
+
208
+ ## Architecture
209
+
210
+ ```
211
+ OpenCode ←→ Plugin (this repo) ←→ Claude-Mem Worker (port 37777) ←→ SQLite + ChromaDB
212
+ ```
213
+
214
+ The plugin is a thin HTTP client. All heavy lifting (LLM processing, storage, search) happens in the worker.
215
+
216
+ ### Hook Mapping (Claude Code → OpenCode)
217
+
218
+ | Claude Code Hook | OpenCode Hook | Purpose |
219
+ |-----------------|---------------|---------|
220
+ | `SessionStart` → context | `experimental.chat.system.transform` | Inject memory into system prompt |
221
+ | `UserPromptSubmit` → session-init | `chat.message` | Init session with real user prompt |
222
+ | `PostToolUse` → observation | `tool.execute.after` | Capture tool executions |
223
+ | `Stop` → summarize | `event` (session.idle) | Summarize with real message content |
224
+ | `Stop` → session-complete | `event` (session.idle) | Mark session complete |
225
+
226
+ ### Worker API Endpoints Used
227
+
228
+ | Method | Endpoint | Purpose |
229
+ |--------|----------|---------|
230
+ | `GET` | `/api/health` | Health check |
231
+ | `GET` | `/api/context/inject?project={name}` | Get formatted context for system prompt |
232
+ | `POST` | `/api/sessions/init` | Initialize session |
233
+ | `POST` | `/api/sessions/observations` | Send tool observation |
234
+ | `POST` | `/api/sessions/summarize` | Trigger summarization |
235
+ | `POST` | `/api/sessions/complete` | Complete session |
236
+ | `GET` | `/api/search?q={query}&project={name}` | Search memory |
237
+
238
+ ## Key Implementation Details
239
+
240
+ - **Field name**: All worker API calls use `contentSessionId` (not `claudeSessionId`) — using the wrong field name causes silent failures
241
+ - **No console output**: All `console.log/warn/error` removed to avoid corrupting OpenCode TUI
242
+ - **Deferred toast**: `client.tui.showToast()` is deferred to first hook invocation (TUI not ready during plugin init, calling it crashes OpenCode)
243
+ - **Real prompt**: `chat.message` hook extracts actual user input instead of hardcoded "SESSION_START"
244
+ - **Real summarize**: `session.idle` fetches last user/assistant messages via `client.session.messages()` for meaningful summaries
245
+
246
+ ## Troubleshooting
247
+
248
+ | Symptom | Cause | Fix |
249
+ |---------|-------|-----|
250
+ | No toast on startup | Worker not running | Start Claude Code first, or check `curl http://127.0.0.1:37777/api/health` |
251
+ | "Worker offline" toast | Worker crashed or not started | Restart Claude Code |
252
+ | OpenCode crashes on startup | Plugin calling TUI too early | Update to latest version (deferred toast fix) |
253
+ | TUI display corrupted | console.log in plugin code | Ensure no console output in source |
254
+ | "SESSION_START" in prompts | Old version bug | Update — `session.created` no longer calls sessionInit |
255
+ | Chroma sync failed | Python version or Chroma not running | See [Windows-Specific Setup](#windows-specific-setup) |
256
+ | Observations not saved | Wrong field name | Ensure `contentSessionId` (not `claudeSessionId`) |
257
+
258
+ ## License
259
+
260
+ MIT
@@ -0,0 +1,14 @@
1
+ import { type Plugin } from '@opencode-ai/plugin';
2
+ /**
3
+ * OpenCode Plugin for Claude-Mem
4
+ *
5
+ * Hooks used:
6
+ * - `event` — session lifecycle (session.created, session.idle)
7
+ * - `tool.execute.after` — capture tool observations
8
+ * - `experimental.chat.system.transform` — inject memory context into system prompt
9
+ * - `chat.message` — fallback session init when event hook doesn't fire
10
+ * - `tool` — mem-search custom tool
11
+ *
12
+ * Uses TUI toast notifications for status feedback.
13
+ */
14
+ export declare const ClaudeMemPlugin: Plugin;
package/dist/index.js ADDED
@@ -0,0 +1,258 @@
1
+ // @bun
2
+ // src/index.ts
3
+ import { tool } from "@opencode-ai/plugin";
4
+
5
+ // src/worker-client.ts
6
+ class WorkerClient {
7
+ static PORT = 37777;
8
+ static BASE_URL = `http://127.0.0.1:${WorkerClient.PORT}`;
9
+ static async isHealthy() {
10
+ try {
11
+ const controller = new AbortController;
12
+ const timeoutId = setTimeout(() => controller.abort(), 1000);
13
+ const response = await fetch(`${this.BASE_URL}/api/health`, {
14
+ signal: controller.signal
15
+ });
16
+ clearTimeout(timeoutId);
17
+ return response.ok;
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+ static async ensureRunning(_projectRoot) {
23
+ return await this.isHealthy();
24
+ }
25
+ static async sessionInit(contentSessionId, project, prompt) {
26
+ try {
27
+ const response = await fetch(`${this.BASE_URL}/api/sessions/init`, {
28
+ method: "POST",
29
+ headers: { "Content-Type": "application/json" },
30
+ body: JSON.stringify({ contentSessionId, project, prompt })
31
+ });
32
+ if (!response.ok) {
33
+ return null;
34
+ }
35
+ return await response.json();
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+ static async sendObservation(contentSessionId, toolName, toolInput, toolResponse, cwd) {
41
+ try {
42
+ await fetch(`${this.BASE_URL}/api/sessions/observations`, {
43
+ method: "POST",
44
+ headers: { "Content-Type": "application/json" },
45
+ body: JSON.stringify({
46
+ contentSessionId,
47
+ tool_name: toolName,
48
+ tool_input: toolInput,
49
+ tool_response: toolResponse,
50
+ cwd
51
+ })
52
+ });
53
+ } catch {}
54
+ }
55
+ static async summarize(contentSessionId, lastUserMessage, lastAssistantMessage) {
56
+ try {
57
+ await fetch(`${this.BASE_URL}/api/sessions/summarize`, {
58
+ method: "POST",
59
+ headers: { "Content-Type": "application/json" },
60
+ body: JSON.stringify({
61
+ contentSessionId,
62
+ last_user_message: lastUserMessage,
63
+ last_assistant_message: lastAssistantMessage
64
+ })
65
+ });
66
+ } catch {}
67
+ }
68
+ static async completeSession(contentSessionId) {
69
+ try {
70
+ await fetch(`${this.BASE_URL}/api/sessions/complete`, {
71
+ method: "POST",
72
+ headers: { "Content-Type": "application/json" },
73
+ body: JSON.stringify({ contentSessionId })
74
+ });
75
+ } catch {}
76
+ }
77
+ static async getContext(project) {
78
+ try {
79
+ const response = await fetch(`${this.BASE_URL}/api/context/inject?project=${encodeURIComponent(project)}`);
80
+ if (!response.ok) {
81
+ return null;
82
+ }
83
+ const data = await response.json();
84
+ if (typeof data === "string") {
85
+ return data;
86
+ }
87
+ if (data && typeof data.content === "string") {
88
+ return data.content;
89
+ }
90
+ const text = JSON.stringify(data, null, 2);
91
+ return text === "{}" || text === "null" ? null : text;
92
+ } catch {
93
+ return null;
94
+ }
95
+ }
96
+ static async search(query, project) {
97
+ try {
98
+ const response = await fetch(`${this.BASE_URL}/api/search?q=${encodeURIComponent(query)}&project=${encodeURIComponent(project)}`);
99
+ if (!response.ok) {
100
+ return "Search failed";
101
+ }
102
+ const data = await response.json();
103
+ return JSON.stringify(data, null, 2);
104
+ } catch (error) {
105
+ return `Error performing search: ${error}`;
106
+ }
107
+ }
108
+ }
109
+
110
+ // src/index.ts
111
+ var ClaudeMemPlugin = async (ctx) => {
112
+ const { project, directory, client } = ctx;
113
+ const projectRoot = directory || process.cwd();
114
+ const projectName = project?.worktree ? project.worktree.split(/[\\/]/).findLast(Boolean) || "unknown-project" : "unknown-project";
115
+ async function toast(message, variant = "info", duration = 3000) {
116
+ try {
117
+ await client.tui.showToast({
118
+ body: { title: "Claude-Mem", message, variant, duration }
119
+ });
120
+ } catch {}
121
+ }
122
+ let workerHealthy = null;
123
+ let initToastShown = false;
124
+ async function checkWorkerAndToast() {
125
+ if (workerHealthy === null) {
126
+ workerHealthy = await WorkerClient.isHealthy();
127
+ }
128
+ if (!initToastShown) {
129
+ initToastShown = true;
130
+ if (workerHealthy) {
131
+ await toast(`Memory active \xB7 ${projectName}`, "success");
132
+ } else {
133
+ await toast("Worker offline \u2014 start Claude Code first", "warning", 5000);
134
+ }
135
+ }
136
+ return workerHealthy;
137
+ }
138
+ let currentSessionId = null;
139
+ const initializedSessions = new Set;
140
+ async function ensureSessionInit(sessionId, prompt) {
141
+ if (initializedSessions.has(sessionId)) {
142
+ return true;
143
+ }
144
+ const isHealthy = await checkWorkerAndToast();
145
+ if (!isHealthy) {
146
+ return false;
147
+ }
148
+ try {
149
+ await WorkerClient.sessionInit(sessionId, projectName, prompt || "SESSION_START");
150
+ initializedSessions.add(sessionId);
151
+ currentSessionId = sessionId;
152
+ return true;
153
+ } catch {
154
+ return false;
155
+ }
156
+ }
157
+ function extractTextFromParts(parts) {
158
+ if (!parts || !Array.isArray(parts)) {
159
+ return "";
160
+ }
161
+ return parts.filter((p) => p.type === "text" && typeof p.text === "string").map((p) => p.text).join(`
162
+ `).trim();
163
+ }
164
+ return {
165
+ event: async ({ event }) => {
166
+ if (event.type === "session.created") {
167
+ const sessionId = event.properties?.info?.id;
168
+ if (!sessionId) {
169
+ return;
170
+ }
171
+ currentSessionId = sessionId;
172
+ const isHealthy = await checkWorkerAndToast();
173
+ if (isHealthy) {
174
+ await toast("Session linked to memory", "info", 2000);
175
+ }
176
+ }
177
+ if (event.type === "session.idle") {
178
+ const sessionId = event.properties?.sessionID || currentSessionId;
179
+ if (!sessionId) {
180
+ return;
181
+ }
182
+ try {
183
+ let lastUserMessage = "";
184
+ let lastAssistantMessage = "";
185
+ try {
186
+ const result = await client.session.messages({
187
+ path: { id: sessionId }
188
+ });
189
+ if (result.data && Array.isArray(result.data)) {
190
+ const messages = result.data;
191
+ for (let i = messages.length - 1;i >= 0; i--) {
192
+ if (messages[i].info.role === "user") {
193
+ lastUserMessage = extractTextFromParts(messages[i].parts);
194
+ break;
195
+ }
196
+ }
197
+ for (let i = messages.length - 1;i >= 0; i--) {
198
+ if (messages[i].info.role === "assistant") {
199
+ lastAssistantMessage = extractTextFromParts(messages[i].parts);
200
+ break;
201
+ }
202
+ }
203
+ }
204
+ } catch {}
205
+ await WorkerClient.summarize(sessionId, lastUserMessage, lastAssistantMessage);
206
+ await WorkerClient.completeSession(sessionId);
207
+ await toast("Session summarized", "success", 2000);
208
+ } catch {}
209
+ }
210
+ },
211
+ "chat.message": async (input, output) => {
212
+ const sessionId = input.sessionID;
213
+ if (sessionId) {
214
+ const userPrompt = extractTextFromParts(output.parts);
215
+ await ensureSessionInit(sessionId, userPrompt || undefined);
216
+ }
217
+ },
218
+ "experimental.chat.system.transform": async (input, output) => {
219
+ const sessionId = input.sessionID;
220
+ if (sessionId) {
221
+ await ensureSessionInit(sessionId);
222
+ }
223
+ const isHealthy = await checkWorkerAndToast();
224
+ if (!isHealthy) {
225
+ return;
226
+ }
227
+ try {
228
+ const context = await WorkerClient.getContext(projectName);
229
+ if (context) {
230
+ output.system.push(`[Claude-Mem] Memory Active. Previous Context:
231
+ ${context}`);
232
+ }
233
+ } catch {}
234
+ },
235
+ "tool.execute.after": async (input, output) => {
236
+ const sessionId = input.sessionID || currentSessionId;
237
+ if (!sessionId) {
238
+ return;
239
+ }
240
+ await ensureSessionInit(sessionId);
241
+ try {
242
+ await WorkerClient.sendObservation(sessionId, input.tool, input.args || {}, output.output, projectRoot);
243
+ } catch {}
244
+ },
245
+ tool: {
246
+ "mem-search": tool({
247
+ description: "Search project history and memory. Use this to find information about past decisions, code changes, or bug fixes.",
248
+ args: {
249
+ query: tool.schema.string()
250
+ },
251
+ execute: async (args) => await WorkerClient.search(args.query, projectName)
252
+ })
253
+ }
254
+ };
255
+ };
256
+ export {
257
+ ClaudeMemPlugin
258
+ };
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Worker Client for Claude-Mem
3
+ * Handles communication with the local worker service running on port 37777.
4
+ */
5
+ export declare class WorkerClient {
6
+ private static readonly PORT;
7
+ private static readonly BASE_URL;
8
+ /**
9
+ * Check if the worker is healthy
10
+ */
11
+ static isHealthy(): Promise<boolean>;
12
+ /**
13
+ * Ensure the worker is running.
14
+ */
15
+ static ensureRunning(_projectRoot: string): Promise<boolean>;
16
+ /**
17
+ * Initialize a session
18
+ */
19
+ static sessionInit(contentSessionId: string, project: string, prompt: string): Promise<{
20
+ sessionDbId: number;
21
+ promptNumber: number;
22
+ } | null>;
23
+ /**
24
+ * Send observation
25
+ */
26
+ static sendObservation(contentSessionId: string, toolName: string, toolInput: any, toolResponse: any, cwd: string): Promise<void>;
27
+ /**
28
+ * Trigger summarization
29
+ */
30
+ static summarize(contentSessionId: string, lastUserMessage: string, lastAssistantMessage: string): Promise<void>;
31
+ /**
32
+ * Complete session
33
+ */
34
+ static completeSession(contentSessionId: string): Promise<void>;
35
+ /**
36
+ * Get pre-formatted context for system prompt injection.
37
+ * Uses /api/context/inject which returns rich markdown with observations + session summaries.
38
+ */
39
+ static getContext(project: string): Promise<string | null>;
40
+ /**
41
+ * Perform Search (used by mem-search tool)
42
+ */
43
+ static search(query: string, project: string): Promise<string>;
44
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@ephemushroom/opencode-claude-mem",
3
+ "version": "0.1.0",
4
+ "description": "OpenCode plugin for Claude-Mem persistent memory system",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "type": "module",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.js"
15
+ }
16
+ },
17
+ "scripts": {
18
+ "build": "bun build src/index.ts --outfile dist/index.js --target bun --format esm --external @opencode-ai/plugin && tsc --emitDeclarationOnly",
19
+ "dev": "tsc --watch",
20
+ "clean": "rm -rf dist",
21
+ "lint": "oxlint",
22
+ "fmt": "oxfmt --write src/",
23
+ "fmt:check": "oxfmt --check src/",
24
+ "typecheck": "tsc --noEmit",
25
+ "prepublishOnly": "bun run clean && bun run build"
26
+ },
27
+ "keywords": [
28
+ "opencode",
29
+ "claude-mem",
30
+ "memory",
31
+ "plugin",
32
+ "persistent-memory"
33
+ ],
34
+ "author": "",
35
+ "license": "MIT",
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/Ephemushroom/opencode-claude-mem.git"
39
+ },
40
+ "bugs": {
41
+ "url": "https://github.com/Ephemushroom/opencode-claude-mem/issues"
42
+ },
43
+ "homepage": "https://github.com/Ephemushroom/opencode-claude-mem#readme",
44
+ "publishConfig": {
45
+ "access": "public"
46
+ },
47
+ "dependencies": {
48
+ "@opencode-ai/plugin": "latest"
49
+ },
50
+ "devDependencies": {
51
+ "@types/bun": "latest",
52
+ "oxfmt": "^0.33.0",
53
+ "oxlint": "^1.48.0",
54
+ "typescript": "^5.0.0"
55
+ }
56
+ }