opencode-claude-memory 1.1.0 β†’ 1.2.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,69 +1,29 @@
1
1
  <div align="center">
2
2
 
3
- # 🧠 opencode-claude-memory
3
+ # 🧠 Claude Code-compatible memory for OpenCode
4
4
 
5
- **A 1:1 replica of [Claude Code's memory system](https://github.com/anthropics/claude-code) for OpenCode**
5
+ **Make OpenCode and Claude Code share the same memory β€” zero config, local-first, and no migration required.**
6
6
 
7
- *Ported from the original source β€” same paths, same format, same tools, same prompts. Zero drift.*
8
-
9
- Claude Code writes memories β†’ OpenCode reads them. OpenCode writes memories β†’ Claude Code reads them.
7
+ Claude Code writes memory β†’ OpenCode reads it. OpenCode writes memory β†’ Claude Code reads it.
10
8
 
11
9
  [![npm version](https://img.shields.io/npm/v/opencode-claude-memory.svg?style=flat-square)](https://www.npmjs.com/package/opencode-claude-memory)
12
10
  [![npm downloads](https://img.shields.io/npm/dm/opencode-claude-memory.svg?style=flat-square)](https://www.npmjs.com/package/opencode-claude-memory)
13
11
  [![License](https://img.shields.io/npm/l/opencode-claude-memory.svg?style=flat-square)](https://github.com/kuitos/opencode-claude-memory/blob/main/LICENSE)
14
12
 
15
- [Features](#-features) β€’ [Quick Start](#-quick-start) β€’ [How It Works](#-how-it-works) β€’ [Configuration](#%EF%B8%8F-configuration) β€’ [Tools Reference](#-tools-reference)
13
+ [Quick Start](#-quick-start) β€’ [Why this exists](#-why-this-exists) β€’ [What makes this different](#-what-makes-this-different) β€’ [How it works](#-how-it-works) β€’ [Who this is for](#-who-this-is-for) β€’ [FAQ](#-faq)
16
14
 
17
15
  </div>
18
16
 
19
17
  ---
20
18
 
21
- ## ✨ Features
22
-
23
- <table>
24
- <tr>
25
- <td width="50%">
26
-
27
- ### πŸ”„ Claude Code Compatible
28
- Shares the exact same `~/.claude/projects/<project>/memory/` directory β€” bidirectional sync out of the box
29
-
30
- </td>
31
- <td width="50%">
32
-
33
- ### πŸ› οΈ 5 Memory Tools
34
- `memory_save`, `memory_delete`, `memory_list`, `memory_search`, `memory_read`
35
-
36
- </td>
37
- </tr>
38
- <tr>
39
- <td width="50%">
40
-
41
- ### ⚑ Auto-Extraction
42
- Drop-in `opencode` wrapper that extracts memories in the background after each session
43
-
44
- </td>
45
- <td width="50%">
46
-
47
- ### πŸ’‰ System Prompt Injection
48
- Existing memories are automatically injected into every conversation
49
-
50
- </td>
51
- </tr>
52
- <tr>
53
- <td width="50%">
54
-
55
- ### πŸ“ 4 Memory Types
56
- `user`, `feedback`, `project`, `reference` β€” same taxonomy as Claude Code
57
-
58
- </td>
59
- <td width="50%">
60
-
61
- ### 🌳 Git Worktree Aware
62
- Worktrees of the same repo share the same memory directory
19
+ ## ✨ At a glance
63
20
 
64
- </td>
65
- </tr>
66
- </table>
21
+ - **Claude Code-compatible memory**
22
+ Uses Claude Code’s existing memory paths, file format, and taxonomy.
23
+ - **Zero config**
24
+ Install + enable plugin, then keep using `opencode` as usual.
25
+ - **Local-first, no migration**
26
+ Memory stays as local Markdown files in the same directory Claude Code already uses.
67
27
 
68
28
  ## πŸš€ Quick Start
69
29
 
@@ -71,11 +31,13 @@ Worktrees of the same repo share the same memory directory
71
31
 
72
32
  ```bash
73
33
  npm install -g opencode-claude-memory
34
+ opencode-memory install # one-time: installs shell hook
74
35
  ```
75
36
 
76
37
  This installs:
77
38
  - The **plugin** β€” memory tools + system prompt injection
78
- - An `opencode` **wrapper** β€” auto-extracts memories after each session
39
+ - The `opencode-memory` **CLI** β€” wraps opencode with post-session memory extraction
40
+ - A **shell hook** β€” defines an `opencode()` function in your `.zshrc`/`.bashrc` that delegates to `opencode-memory`
79
41
 
80
42
  ### 2. Configure
81
43
 
@@ -89,84 +51,110 @@ This installs:
89
51
  ### 3. Use
90
52
 
91
53
  ```bash
92
- opencode # just use it as usual
54
+ opencode
93
55
  ```
94
56
 
95
- The AI agent can now use memory tools:
57
+ That’s it. Memory extraction runs in the background after each session.
96
58
 
97
- - **"Remember that I prefer terse responses"** β†’ saves a `feedback` memory
98
- - **"What do you remember about me?"** β†’ reads from memory
99
- - **"Forget the memory about my role"** β†’ deletes a memory
100
-
101
- When you exit, memories are extracted in the background β€” zero blocking.
102
-
103
- <details>
104
- <summary>πŸ—‘οΈ Uninstall</summary>
59
+ To uninstall:
105
60
 
106
61
  ```bash
62
+ opencode-memory uninstall # removes shell hook from .zshrc/.bashrc
107
63
  npm uninstall -g opencode-claude-memory
108
64
  ```
109
65
 
110
- This removes the wrapper and the plugin. Your saved memories in `~/.claude/projects/` are **not** deleted.
66
+ This removes the shell hook, the CLI, and the plugin. Your saved memories in `~/.claude/projects/` are **not** deleted.
67
+
68
+ ## πŸ’‘ Why this exists
69
+
70
+ If you use both Claude Code and OpenCode on the same repository, memory often ends up in separate silos.
71
+
72
+ This project solves that by making OpenCode read and write memory in Claude Code’s existing structure, so your context carries over naturally between both tools.
73
+
74
+ ## 🧩 What makes this different
111
75
 
112
- </details>
76
+ Most memory plugins introduce a new storage model or migration step.
113
77
 
114
- ## πŸ’‘ How It Works
78
+ This one is a **compatibility layer**, not a new memory system:
79
+
80
+ - same memory directory conventions as Claude Code
81
+ - same Markdown + frontmatter format
82
+ - same memory taxonomy (`user`, `feedback`, `project`, `reference`)
83
+ - same project/worktree resolution behavior
84
+
85
+ The outcome: **shared context across Claude Code and OpenCode without maintaining two memory systems.**
86
+
87
+ ## βš™οΈ How it works
115
88
 
116
89
  ```mermaid
117
90
  graph LR
118
- A[You run opencode] --> B[Wrapper finds real binary]
119
- B --> C[Runs opencode normally]
120
- C --> D[You exit]
121
- D --> E[Get latest session ID]
91
+ A[You run opencode] --> B[Shell hook calls opencode-memory]
92
+ B --> C[opencode-memory finds real binary]
93
+ C --> D[Runs opencode normally]
94
+ D --> E[You exit]
122
95
  E --> F[Fork session + extract memories]
123
96
  F --> G[Memories saved to ~/.claude/projects/]
124
97
  ```
125
98
 
126
- The wrapper is a drop-in replacement that:
99
+ The shell hook defines an `opencode()` function that delegates to `opencode-memory`:
100
+
101
+ 1. Shell function intercepts `opencode` command (higher priority than PATH)
102
+ 2. `opencode-memory` finds the real `opencode` binary in PATH
103
+ 3. Runs it with all your arguments
104
+ 4. After you exit, forks the session with a memory extraction prompt
105
+ 5. Extraction runs **in the background** β€” you're never blocked
106
+
107
+ ### Compatibility details
108
+
109
+ The implementation ports core logic from Claude Code for path hashing, git-root/worktree handling, memory format, and memory prompting behavior, so both tools can operate on the same files safely.
127
110
 
128
- 1. Scans `PATH` to find the real `opencode` binary (skipping itself)
129
- 2. Runs it with all your arguments
130
- 3. After you exit, forks the session with a memory extraction prompt
131
- 4. Extraction runs **in the background** β€” you're never blocked
111
+ ## πŸ‘₯ Who this is for
132
112
 
133
- ### What "1:1 Replica" Means
113
+ - You use **both Claude Code and OpenCode**.
114
+ - You want **one shared memory context** across both tools.
115
+ - You prefer **file-based, local-first memory** you can inspect in Git/worktrees.
116
+ - You don’t want migration overhead or lock-in.
134
117
 
135
- Every core component is ported directly from [Claude Code's source](https://github.com/anthropics/claude-code):
118
+ ## ❓ FAQ
136
119
 
137
- | Component | Source |
138
- |---|---|
139
- | `sanitizePath()` + `djb2Hash()` | `utils/sessionStoragePortable.ts` |
140
- | `findGitRoot()` + worktree resolution | `utils/git.ts` |
141
- | Memory types & frontmatter format | `commands/memory.ts` |
142
- | System prompt (types, when to save/skip) | `commands/memory.ts` |
143
- | Extraction prompt (post-session) | Claude Code's memory extraction agent |
120
+ ### Is this a new memory system?
144
121
 
145
- This ensures:
146
- - `~/.claude/projects/<sanitized>/memory/` paths are **byte-identical** to Claude Code's output
147
- - Git worktrees resolve to the same canonical root
148
- - Memory files are interchangeable β€” no migration needed
122
+ No. It is a compatibility layer that lets OpenCode use Claude Code-compatible memory layout and conventions.
149
123
 
150
- ## βš™οΈ Configuration
124
+ ### Do I need to migrate existing memory?
151
125
 
152
- ### Environment Variables
126
+ No migration required. If you already have Claude Code memory files, OpenCode can work with them directly.
153
127
 
154
- | Variable | Default | Description |
155
- |---|---|---|
156
- | `OPENCODE_MEMORY_EXTRACT` | `1` | Set to `0` to disable auto-extraction |
157
- | `OPENCODE_MEMORY_FOREGROUND` | `0` | Set to `1` to run extraction in foreground (debugging) |
158
- | `OPENCODE_MEMORY_MODEL` | *(default)* | Override model for extraction |
159
- | `OPENCODE_MEMORY_AGENT` | *(default)* | Override agent for extraction |
128
+ ### Where is data stored?
129
+
130
+ In local files under Claude-style project memory directories (for example, under `~/.claude/projects/<project>/memory/`).
131
+
132
+ ### Why file-based memory?
133
+
134
+ File-based memory is transparent, local-first, easy to inspect/diff/back up, and works naturally with existing developer workflows.
135
+
136
+ ### Can I disable auto extraction?
137
+
138
+ Yes. Set `OPENCODE_MEMORY_EXTRACT=0`.
139
+
140
+ ## πŸ”§ Configuration
141
+
142
+ ### Environment variables
143
+
144
+ - `OPENCODE_MEMORY_EXTRACT` (default `1`): set `0` to disable auto-extraction
145
+ - `OPENCODE_MEMORY_FOREGROUND` (default `0`): set `1` to run extraction in foreground
146
+ - `OPENCODE_MEMORY_MODEL`: override model used for extraction
147
+ - `OPENCODE_MEMORY_AGENT`: override agent used for extraction
160
148
 
161
149
  ### Logs
162
150
 
163
151
  Extraction logs are written to `$TMPDIR/opencode-memory-logs/extract-*.log`.
164
152
 
165
- ### Concurrency Safety
153
+ ### Concurrency safety
166
154
 
167
- A file lock prevents multiple extractions from running simultaneously on the same project. Stale locks (from crashed processes) are automatically cleaned up.
155
+ A file lock prevents multiple extractions from running simultaneously on the same project. Stale locks are cleaned up automatically.
168
156
 
169
- ## πŸ“ Memory Format
157
+ ## πŸ“ Memory format
170
158
 
171
159
  Each memory is a Markdown file with YAML frontmatter:
172
160
 
@@ -183,56 +171,19 @@ Skip post-action summaries. User reads diffs directly.
183
171
  **How to apply:** Don't summarize changes at the end of responses.
184
172
  ```
185
173
 
186
- ### Memory Types
187
-
188
- | Type | Description |
189
- |---|---|
190
- | `user` | User's role, expertise, preferences |
191
- | `feedback` | Guidance on how to work (corrections and confirmations) |
192
- | `project` | Ongoing work context not derivable from code |
193
- | `reference` | Pointers to external resources |
194
-
195
- <details>
196
- <summary>πŸ“„ Index file (MEMORY.md)</summary>
197
-
198
- `MEMORY.md` is an auto-managed index (not content storage). Each entry is one line:
199
-
200
- ```markdown
201
- - [User prefers terse responses](feedback_terse_responses.md) β€” Skip summaries, user reads diffs
202
- - [User is a data scientist](user_role.md) β€” Focus on observability/logging context
203
- ```
204
-
205
- </details>
206
-
207
- ## πŸ”§ Tools Reference
208
-
209
- ### `memory_save`
210
-
211
- Save or update a memory.
212
-
213
- | Parameter | Type | Required | Description |
214
- |---|---|---|---|
215
- | `file_name` | string | βœ… | File name slug (e.g., `user_role`) |
216
- | `name` | string | βœ… | Short title |
217
- | `description` | string | βœ… | One-line description for relevance matching |
218
- | `type` | enum | βœ… | `user`, `feedback`, `project`, or `reference` |
219
- | `content` | string | βœ… | Memory content |
220
-
221
- ### `memory_delete`
222
-
223
- Delete a memory by file name.
224
-
225
- ### `memory_list`
226
-
227
- List all memories with their metadata.
228
-
229
- ### `memory_search`
230
-
231
- Search memories by keyword across name, description, and content.
174
+ Supported memory types:
175
+ - `user`
176
+ - `feedback`
177
+ - `project`
178
+ - `reference`
232
179
 
233
- ### `memory_read`
180
+ ## πŸ”§ Tools reference
234
181
 
235
- Read the full content of a specific memory file.
182
+ - `memory_save`: save/update a memory
183
+ - `memory_delete`: delete a memory by filename
184
+ - `memory_list`: list memory metadata
185
+ - `memory_search`: search by keyword
186
+ - `memory_read`: read full memory content
236
187
 
237
188
  ## πŸ“„ License
238
189
 
@@ -1,23 +1,25 @@
1
1
  #!/usr/bin/env bash
2
2
  #
3
- # opencode (memory wrapper) β€” Drop-in replacement that wraps the real opencode
4
- # binary, then automatically extracts and saves memories after the session ends.
3
+ # opencode-memory β€” Wrapper for OpenCode with automatic memory extraction.
5
4
  #
6
- # Install by placing this earlier in PATH than the real opencode binary.
7
- # The script finds the real opencode by scanning PATH and skipping itself.
5
+ # Installs a shell hook (function) that intercepts the `opencode` command,
6
+ # then wraps the real binary with post-session memory extraction.
8
7
  #
9
- # Usage:
10
- # opencode [any opencode args...]
8
+ # Subcommands:
9
+ # opencode-memory install β€” Install shell hook to ~/.zshrc or ~/.bashrc
10
+ # opencode-memory uninstall β€” Remove shell hook
11
+ # opencode-memory [args...] β€” Run opencode with memory extraction
11
12
  #
12
13
  # How it works:
13
- # 1. Finds the real `opencode` binary (skipping this wrapper in PATH)
14
- # 2. Runs it normally with all your arguments
15
- # 3. After you exit, finds the most recent session
16
- # 4. Forks that session and sends a memory extraction prompt
17
- # 5. The extraction runs in the background so you're not blocked
14
+ # 1. Shell hook defines `opencode()` function that delegates to `opencode-memory`
15
+ # 2. `opencode-memory` finds the real `opencode` binary in PATH
16
+ # 3. Runs it normally with all your arguments
17
+ # 4. After you exit, finds the most recent session
18
+ # 5. Forks that session and sends a memory extraction prompt
19
+ # 6. The extraction runs in the background so you're not blocked
18
20
  #
19
21
  # Requirements:
20
- # - Real `opencode` CLI reachable in PATH (after this wrapper)
22
+ # - Real `opencode` CLI reachable in PATH
21
23
  # - `jq` for JSON parsing
22
24
  # - The opencode-memory plugin installed (provides memory_save tool)
23
25
  #
@@ -32,23 +34,121 @@
32
34
  set -euo pipefail
33
35
 
34
36
  # ============================================================================
35
- # Resolve the real opencode binary (skip this wrapper)
37
+ # Shell Hook Management
36
38
  # ============================================================================
37
39
 
38
- SELF="$(cd "$(dirname "$0")" && pwd -P)/$(basename "$0")"
40
+ HOOK_START_MARKER='# >>> opencode-memory auto-initialization >>>'
41
+ HOOK_END_MARKER='# <<< opencode-memory auto-initialization <<<'
42
+
43
+ detect_shell_rc() {
44
+ local shell_name
45
+ shell_name="$(basename "${SHELL:-}")"
46
+
47
+ case "$shell_name" in
48
+ zsh)
49
+ echo "$HOME/.zshrc"
50
+ ;;
51
+ bash)
52
+ echo "$HOME/.bashrc"
53
+ ;;
54
+ *)
55
+ if [ -f "$HOME/.zshrc" ]; then
56
+ echo "$HOME/.zshrc"
57
+ elif [ -f "$HOME/.bashrc" ]; then
58
+ echo "$HOME/.bashrc"
59
+ else
60
+ echo "$HOME/.zshrc"
61
+ fi
62
+ ;;
63
+ esac
64
+ }
39
65
 
40
- find_real_opencode() {
41
- local IFS=':'
42
- for dir in $PATH; do
43
- local candidate="$dir/opencode"
44
- if [ -x "$candidate" ] && [ "$(cd "$(dirname "$candidate")" && pwd -P)/$(basename "$candidate")" != "$SELF" ]; then
45
- echo "$candidate"
46
- return 0
66
+ install_hook() {
67
+ local rc_file
68
+ rc_file=$(detect_shell_rc)
69
+
70
+ if grep -qF "$HOOK_START_MARKER" "$rc_file" 2>/dev/null; then
71
+ echo "[opencode-memory] Hook already installed in $rc_file"
72
+ return 0
73
+ fi
74
+
75
+ cat >> "$rc_file" << 'HOOK'
76
+
77
+ # >>> opencode-memory auto-initialization >>>
78
+ opencode() {
79
+ command opencode-memory "$@"
80
+ }
81
+ # <<< opencode-memory auto-initialization <<<
82
+ HOOK
83
+
84
+ echo "[opencode-memory] Shell hook installed in $rc_file"
85
+ echo "[opencode-memory] Restart your shell or run: source $rc_file"
86
+ }
87
+
88
+ remove_hook_from_rc() {
89
+ local rc_file="$1"
90
+ local tmp_file
91
+ tmp_file=$(mktemp)
92
+
93
+ awk -v start="$HOOK_START_MARKER" -v end="$HOOK_END_MARKER" '
94
+ $0 == start { skip=1; next }
95
+ $0 == end { skip=0; next }
96
+ !skip
97
+ ' "$rc_file" > "$tmp_file"
98
+
99
+ mv "$tmp_file" "$rc_file"
100
+ }
101
+
102
+ uninstall_hook() {
103
+ local removed=0
104
+ local rc_file
105
+
106
+ for rc_file in "$HOME/.zshrc" "$HOME/.bashrc"; do
107
+ [ -f "$rc_file" ] || continue
108
+
109
+ if grep -qF "$HOOK_START_MARKER" "$rc_file" 2>/dev/null; then
110
+ remove_hook_from_rc "$rc_file"
111
+ echo "[opencode-memory] Shell hook removed from $rc_file"
112
+ removed=1
47
113
  fi
48
114
  done
49
- echo "[opencode-memory] ERROR: Cannot find real opencode binary in PATH" >&2
50
- echo "[opencode-memory] Make sure opencode is installed and this wrapper is placed earlier in PATH" >&2
51
- exit 1
115
+
116
+ if [ "$removed" -eq 0 ]; then
117
+ rc_file=$(detect_shell_rc)
118
+ echo "[opencode-memory] Hook not found in $rc_file"
119
+ return 0
120
+ fi
121
+
122
+ echo "[opencode-memory] Restart your shell or run: source <your rc file>"
123
+ }
124
+
125
+ # Handle subcommands before any opencode resolution
126
+ case "${1:-}" in
127
+ install)
128
+ install_hook
129
+ exit 0
130
+ ;;
131
+ uninstall)
132
+ uninstall_hook
133
+ exit 0
134
+ ;;
135
+ esac
136
+
137
+ # ============================================================================
138
+ # Resolve the real opencode binary
139
+ # ============================================================================
140
+
141
+ find_real_opencode() {
142
+ # Since this script is named `opencode-memory` (not `opencode`),
143
+ # `command -v opencode` finds the real binary without ambiguity.
144
+ local real
145
+ real=$(command -v opencode 2>/dev/null) || true
146
+ if [ -z "$real" ] || [ ! -x "$real" ]; then
147
+ echo "[opencode-memory] ERROR: Cannot find opencode binary in PATH" >&2
148
+ echo "[opencode-memory] Make sure opencode is installed: https://opencode.ai" >&2
149
+ exit 1
150
+ fi
151
+ echo "$real"
52
152
  }
53
153
 
54
154
  REAL_OPENCODE="$(find_real_opencode)"
@@ -129,15 +229,15 @@ has_new_memories() {
129
229
  # Check if any memory file was modified during the session
130
230
  # Checks all projects' memory directories for files newer than the timestamp marker
131
231
  local mem_base="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/projects"
132
-
232
+
133
233
  if [ ! -d "$mem_base" ]; then
134
234
  return 1
135
235
  fi
136
-
236
+
137
237
  # Find any .md file under projects/*/memory/ newer than our timestamp
138
238
  local newer_files
139
239
  newer_files=$(find "$mem_base" -path "*/memory/*.md" -newer "$TIMESTAMP_FILE" 2>/dev/null | head -1)
140
-
240
+
141
241
  [ -n "$newer_files" ]
142
242
  }
143
243
 
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "opencode-claude-memory",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
- "description": "Cross-session memory plugin for OpenCode β€” Claude Code compatible, persistent, file-based memory",
5
+ "description": "Claude Code-compatible memory compatibility layer for OpenCode β€” zero config, local-first, no migration",
6
6
  "main": "src/index.ts",
7
7
  "bin": {
8
- "opencode": "./bin/opencode"
8
+ "opencode-memory": "./bin/opencode-memory"
9
9
  },
10
10
  "exports": {
11
11
  ".": "./src/index.ts"